From 530184dd777ab3d19d3315d5a3f2fa9fd54f9c12 Mon Sep 17 00:00:00 2001 From: adsuth Date: Thu, 3 Aug 2023 05:56:51 +0100 Subject: [PATCH 01/60] rough conversion, WIP --- .gitignore | 25 +- LICENSE.md | 21 - README.md | 62 - UNINSTALL.md | 21 - background.js | 4 - content-script.js | 681 --------- icons/byts128.png | Bin 2723 -> 0 bytes icons/byts16.png | Bin 603 -> 0 bytes icons/byts32.png | Bin 1117 -> 0 bytes icons/byts48.png | Bin 1787 -> 0 bytes index.html | 13 + manifest.json | 52 +- package-lock.json | 2109 +++++++++++++++++++++++++++ package.json | 24 + popup.html | 48 - popup.js | 280 ---- src/assets/icons/bys-128.png | Bin 0 -> 8601 bytes src/assets/icons/bys-16.png | Bin 0 -> 2317 bytes src/assets/icons/bys-32.png | Bin 0 -> 3003 bytes src/assets/icons/bys-48.png | Bin 0 -> 3747 bytes src/background.ts | 3 + src/background/browser.ts | 2 + src/background/handleColorScheme.ts | 28 + src/components/Popup.tsx | 58 + src/content.ts | 476 ++++++ src/css/popup.css | 364 +++++ src/css/popup.css.map | 1 + popup.css => src/css/popup.scss | 57 +- src/lib/changeShort.ts | 8 + src/lib/declarations.ts | 128 ++ src/lib/getters.ts | 71 + src/lib/skipShort.ts | 46 + src/lib/types.ts | 15 + src/lib/utils.ts | 70 + src/main.tsx | 17 + src/vite-env.d.ts | 1 + styles.css | 125 -- tsconfig.json | 21 + tsconfig.node.json | 8 + vite.config.ts | 11 + 40 files changed, 3560 insertions(+), 1290 deletions(-) delete mode 100644 LICENSE.md delete mode 100644 README.md delete mode 100644 UNINSTALL.md delete mode 100644 background.js delete mode 100644 content-script.js delete mode 100644 icons/byts128.png delete mode 100644 icons/byts16.png delete mode 100644 icons/byts32.png delete mode 100644 icons/byts48.png create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json delete mode 100644 popup.html delete mode 100644 popup.js create mode 100644 src/assets/icons/bys-128.png create mode 100644 src/assets/icons/bys-16.png create mode 100644 src/assets/icons/bys-32.png create mode 100644 src/assets/icons/bys-48.png create mode 100644 src/background.ts create mode 100644 src/background/browser.ts create mode 100644 src/background/handleColorScheme.ts create mode 100644 src/components/Popup.tsx create mode 100644 src/content.ts create mode 100644 src/css/popup.css create mode 100644 src/css/popup.css.map rename popup.css => src/css/popup.scss (87%) create mode 100644 src/lib/changeShort.ts create mode 100644 src/lib/declarations.ts create mode 100644 src/lib/getters.ts create mode 100644 src/lib/skipShort.ts create mode 100644 src/lib/types.ts create mode 100644 src/lib/utils.ts create mode 100644 src/main.tsx create mode 100644 src/vite-env.d.ts delete mode 100644 styles.css create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore index 6f66c74..a547bf3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,24 @@ -*.zip \ No newline at end of file +# 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/LICENSE.md b/LICENSE.md deleted file mode 100644 index 0211d26..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Ng Young Shung - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index dbb3af8..0000000 --- a/README.md +++ /dev/null @@ -1,62 +0,0 @@ -
- -![BYS Icon](https://raw.githubusercontent.com/ynshung/better-yt-shorts/master/icons/byts128.png) - -# Better YouTube Shorts - -![Current Version](https://img.shields.io/amo/v/better-youtube-shorts?label=version) -[![Chrome Web Store users](https://img.shields.io/chrome-web-store/users/icnidlkdlledahfgejnagmhgaeijokcp?label=chrome)](https://chrome.google.com/webstore/detail/better-youtube-shorts/icnidlkdlledahfgejnagmhgaeijokcp) -[![Firefox Add-on users](https://img.shields.io/amo/users/better-youtube-shorts?label=firefox)](https://addons.mozilla.org/en-US/firefox/addon/better-youtube-shorts) -![Chrome Web Store](https://img.shields.io/chrome-web-store/rating/icnidlkdlledahfgejnagmhgaeijokcp) -![License: MIT](https://img.shields.io/github/license/ynshung/better-yt-shorts) -
- -Control your YouTube shorts just like a normal YouTube video! Features include progress bar, seeking, playback speed, auto skip and more. You can also customize the keybinds to your liking! - - -* Chrome Extension: https://chrome.google.com/webstore/detail/better-youtube-shorts/icnidlkdlledahfgejnagmhgaeijokcp -* Firefox Add-on: https://addons.mozilla.org/en-US/firefox/addon/better-youtube-shorts - -## Features -* **Progress bar** at the bottom with time and duration -* **Seeking** 5 seconds backward and forward with arrow keys -* **Auto skip** short when current one ends -* Auto skip short with likes below custom threshold (e.g. 500 likes) -* Decrease and increase **playback speed** with keys U and O -* Revert to normal speed with I or by clicking the speed button -* Control **volume** with the volume slider or with - and =, mute audio with M -* Mini timestamp and speed above the like button (can be scrolled on!) -* Navigate to next or previous short **without animation** with W and S -* Go to the next **frame** or previous frame with . and , while paused -* **Customizable** keybinds - -### Default Keybinds -| Action | Shortcut | -|----------------------|------------| -| Seek Backward (+5s) | ArrowLeft | -| Seek Forward (-5s) | ArrowRight | -| Decrease Speed | KeyU | -| Reset Speed | KeyI | -| Increase Speed | KeyO | -| Decrease Volume | Minus | -| Increase Volume | Equal | -| Toggle Mute | KeyM | -| Next Frame | Comma | -| Previous Frame | Period | -| Next Short | KeyS | -| Previous Short | KeyW | - -## Screenshots - -![image](https://user-images.githubusercontent.com/80070435/219866197-2401c0d0-2632-45ed-9152-f1024828f46f.png) -![image](https://user-images.githubusercontent.com/80070435/219866370-d1acbd50-049b-47ef-9688-19d1dc4efe91.png) -![image](https://user-images.githubusercontent.com/80070435/219866388-13770811-674d-4681-be32-c7d27f35c000.png) - -## Issues / Suggestion -If you faced any issue with the extension or any suggestion that can help to improve the extension, you may create an issue [here](https://github.com/ynshung/better-yt-shorts/issues) or if you know how to code, fork the repo, make the necessary changes and create a pull request. - -You may leave your feedback in this [Google Form](https://forms.gle/pvSiMwDeQVfwyALfA). - -## License - -MIT License diff --git a/UNINSTALL.md b/UNINSTALL.md deleted file mode 100644 index 97d30fa..0000000 --- a/UNINSTALL.md +++ /dev/null @@ -1,21 +0,0 @@ -# Leaving so soon? - -We're sad to see you go! It would be great if you could let us know why you're uninstalling the extension by leaving feedback below. - -## Help us improve! - -If there's anything we can do to improve your experience, please let us know via the [issue tracker](https://github.com/ynshung/better-yt-shorts/issues) in GitHub. - -If you don't have a GitHub account, you may leave us feedback via this [Google Form](https://forms.gle/pvSiMwDeQVfwyALfA). Please check for existing issues before submitting a new one. - -If you can understand code, feel free to make changes to the code and submit a pull request. - -## Thank you! - -You may reinstall the extension at any time by visiting the [Chrome Web Store](https://chrome.google.com/webstore/detail/better-youtube-shorts/icnidlkdlledahfgejnagmhgaeijokcp) or [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/better-youtube-shorts). - -Thank you for your support, and we hope to see you again in the future! - - - - diff --git a/background.js b/background.js deleted file mode 100644 index e177d49..0000000 --- a/background.js +++ /dev/null @@ -1,4 +0,0 @@ -const browserObj = (typeof browser === 'undefined') ? chrome : browser; -const version = browserObj.runtime.getManifest().version; - -browserObj.runtime.setUninstallURL("https://github.com/ynshung/better-yt-shorts/blob/master/UNINSTALL.md"); \ No newline at end of file diff --git a/content-script.js b/content-script.js deleted file mode 100644 index 3a56da4..0000000 --- a/content-script.js +++ /dev/null @@ -1,681 +0,0 @@ -const defaultKeybinds = { - 'Seek Backward': 'ArrowLeft', - 'Seek Forward': 'ArrowRight', - 'Decrease Speed': 'KeyU', - 'Reset Speed': 'KeyI', - 'Increase Speed': 'KeyO', - 'Decrease Volume': 'Minus', - 'Increase Volume': 'Equal', - 'Toggle Mute': 'KeyM', - 'Next Frame': 'Comma', - 'Previous Frame': 'Period', - 'Next Short': 'KeyS', - 'Previous Short': 'KeyW', -}; -const defaultExtraOptions = { - skip_enabled: false, - skip_threshold: 500, -} -const storage = (typeof browser === 'undefined') ? chrome.storage.local : browser.storage.local; -var muted = false; -var volumeState = 0; -var actualVolume = 0; -var skippedId = null -var topId = 0 // store the furthest id in the chain - -// video with no likes => https://www.youtube.com/shorts/ZFLRydDd9Mw -// video with no likes and 23k comments => https://www.youtube.com/shorts/gISsypl5xsc -// another => https://www.youtube.com/shorts/qe56pgRVrgE?feature=share -// video with 1.5M / 1,5M => https://www.youtube.com/shorts/nKZIx1bHUbQ - -function shouldSkipShort( currentId, likeCount ) -{ - // for debugging purposes - - // console.dir({ - // "extra options check": !( extraOptions == null ), - // "video playing check": !( getVideo().currentTime === 0 ), - // "option enabled?": !( !extraOptions.skip_enabled ), - // "current id check": !( currentId < topId ), - // "skipped id check": !( skippedId === currentId ), - // "likecount null check": !( likeCount === null || isNaN( likeCount ) ), - // "threshold check": !( likeCount >= extraOptions.skip_threshold ), - // "current threshold": extraOptions.skip_threshold, - // "number of likes": likeCount - // }) - - if ( extraOptions === null ) return false - if ( getVideo().currentTime === 0 ) return false // video unstarted, likes likely not loaded - - if ( !extraOptions.skip_enabled ) return false - if ( currentId < topId ) return false // allow user to scroll back up to see skipped video - if ( skippedId === currentId ) return false // prevent skip spam - if ( likeCount === null || isNaN( likeCount ) ) return false // dont skip unloaded shorts - if ( likeCount >= extraOptions.skip_threshold ) return false - return true -} - -/** - * If the setting `shouldSkipUnrecommendedShorts` is true, skip shorts that have fewer than the set number of likes - */ - - // fixed mac scroll issue -function skipShort( short ) -{ - var nextButton = getNextButton(); - nextButton.click(); -} - -// Using localStorage as a fallback for browser/chrome.storage.local -var keybinds = JSON.parse(localStorage.getItem("yt-keybinds")); -storage.get(["keybinds"]) -.then((result) => { - if (result.keybinds) { - // Set default keybinds if not exists - for (const [cmd, keybind] of Object.entries(defaultKeybinds)) { - if (!result.keybinds[cmd]) result.keybinds[cmd] = keybind; - } - if (result.keybinds !== keybinds) localStorage.setItem("yt-keybinds", JSON.stringify(result.keybinds)); - keybinds = result.keybinds; - } -}); - -var extraOptions = JSON.parse(localStorage.getItem("yt-extraopts")) -storage.get( ["extraopts"] ) - .then((result) => { - if (result.extraopts) - { - // Set default options if not exists - for ( const [ option, value ] of Object.entries( defaultExtraOptions ) ) { - if ( result.extraopts[ option ] ) continue - result.extraopts[ option ] = value - } - - if ( result.extraopts !== extraOptions ) - localStorage.setItem("yt-extraopts", JSON.stringify(result.extraopts) ) - - extraOptions = result.extraopts - } - }) - -document.addEventListener("keydown", (data) => { - if ( - document.activeElement === document.querySelector(`input`) || - document.activeElement === document.querySelector("#contenteditable-root") - ) return; // Avoids using keys while the user interacts with any input, like search and comment. - const ytShorts = getVideo(); - if (!ytShorts) return; - if (!keybinds) keybinds = defaultKeybinds; - - const key = data.code; - const keyAlt = data.key.toLowerCase(); // for legacy keybinds - - let command; - for ( const [cmd, keybind] of Object.entries(keybinds) ) - if ( key === keybind || keyAlt === keybind ) - command = cmd; - - if (!command) return; - - switch (command) { - case "Seek Backward": - ytShorts.currentTime -= 5; - break; - - case "Seek Forward": - ytShorts.currentTime += 5; - break; - - case "Decrease Speed": - if (ytShorts.playbackRate > 0.25) ytShorts.playbackRate -= 0.25; - break; - - case "Reset Speed": - ytShorts.playbackRate = 1; - break; - - case "Increase Speed": - if (ytShorts.playbackRate < 16) ytShorts.playbackRate += 0.25; - break; - - case "Increase Volume": - if (ytShorts.volume <= 0.975) { - setVolume(ytShorts.volume + 0.025); - } - break; - - case "Decrease Volume": - if (ytShorts.volume >= 0.025) { - setVolume(ytShorts.volume - 0.025); - } - break; - - case "Toggle Mute": - if (!muted) { - muted = true; - volumeState = ytShorts.volume; - ytShorts.volume = 0; - } else { - muted = false; - ytShorts.volume = volumeState; - } - break; - - case "Next Frame": - if (ytShorts.paused) { - ytShorts.currentTime -= 0.04; - } - break; - - case "Previous Frame": - if (ytShorts.paused) { - ytShorts.currentTime += 0.04; - } - break; - - case "Next Short": - goToNextShort( ytShorts ) - break; - - case "Previous Short": - goToPrevShort( ytShorts ) - break; - } - setSpeed = ytShorts.playbackRate; -}); - -const getCurrentId = () => { - const videoEle = document.querySelector( - "#shorts-player > div.html5-video-container > video" - ); - if (videoEle && videoEle.closest("ytd-reel-video-renderer")) return +videoEle.closest("ytd-reel-video-renderer").id; - return null; -}; - -const getLikeCount = (id) => { - const likesElement = document.querySelector( - `[id='${id}'] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer #like-button` - ); - - // Use optional chaining and nullish coalescing to handle null values - const numberOfLikes = likesElement?.firstElementChild?.innerText.split(/\r?\n/)[0]?.trim().replace(/\s/g, "").replace(/\.$/, "").toLowerCase() ?? "0"; - - // Convert the number of likes to the appropriate format - const likeCount = convertLocaleNumber(numberOfLikes); - - // If likeCount is anything other than a number, it'll return 0. Meaning it'll translate every language. - return !isNaN(likeCount) ? likeCount : "0"; -}; - -// Checking comment count aswell, as sometimes popular videos bug out and show 0 likes, but there's 1000+ comments. -const getCommentCount = (id) => { - const commentsElement = document.querySelector( - `[id='${id}'] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer #comments-button` - ); - - // Use optional chaining and nullish coalescing to handle null values - const numberOfComments = commentsElement?.firstElementChild?.innerText.split(/\r?\n/)[0]?.replace(/ /g, "") ?? "0"; - - // Convert the number of comments to the appropriate format - const commentCount = convertLocaleNumber(numberOfComments); - - // If commentCount is anything other than a number, it'll return 0. Meaning it'll handle every language. - return !isNaN(commentCount) ? commentCount : 0; -}; - -const getActionElement = (id) => - document.querySelector( - `[id='${id}'] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #actions` - ); - -const getOverlayElement = (id) => - document.querySelector( - `[id='${id}'] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #overlay` - ); - -const getVolumeContainer = (id) => - document.querySelector( - `[id='${id}'] > #player-container > div.player-controls.style-scope.ytd-reel-video-renderer > ytd-shorts-player-controls.style-scope.ytd-reel-video-renderer` - ); - -const getNextButton = () => - document.querySelector('#navigation-button-down > .style-scope.ytd-shorts > yt-button-shape > button.yt-spec-button-shape-next.yt-spec-button-shape-next--text.yt-spec-button-shape-next--mono.yt-spec-button-shape-next--size-xl.yt-spec-button-shape-next--icon-button'); - -const setTimer = (currTime, duration) => { - const id = getCurrentId(); - if (document.getElementById(`ytTimer${id}`) === null) return false; - document.getElementById( - `ytTimer${id}` - ).innerText = `${currTime}/${duration}s`; - return true -}; - -const setVolumeSlider = (ytShorts, id) => { - const volumeContainer = getVolumeContainer(id); - const slider = document.createElement("input"); - if(!actualVolume) actualVolume = 0.5; - checkVolume(ytShorts); - slider.id = `volumeSliderController${id}`; - slider.classList.add("volume-slider"); - slider.classList.add("betterYT-volume-slider"); - slider.type = "range"; - slider.min = 0; - slider.max = 1; - slider.step = 0.01; - slider.setAttribute("orient", "vertical"); - volumeContainer.appendChild(slider); - slider.value = actualVolume; - - slider.addEventListener("input", (data) => { - setVolume(data.target.value); - }); - - // Prevent video from pausing/playing on click - slider.addEventListener("click", (data) => { - data.stopPropagation(); - }); -}; - -const setVolume = (volume) => { - const id = getCurrentId() - const volumeSliderController = document.getElementById(`volumeSliderController${id}`); - volumeSliderController.value = volume; - - const ytShorts = document.querySelector( - "#shorts-player > div.html5-video-container > video" - ); - ytShorts.volume = volume; - localStorage.setItem("yt-player-volume",`{ - "data": {\"volume\":`+volume+`,\"muted\":`+muted+`} - }`) -} - -const checkVolume = (ytShorts) => { - if(localStorage.getItem("yt-player-volume") !== null && JSON.parse(localStorage.getItem("yt-player-volume"))["data"]["volume"]){ - actualVolume = JSON.parse(localStorage.getItem("yt-player-volume"))["data"]["volume"]; - ytShorts.volume = actualVolume; - }else{ - actualVolume = ytShorts.volume; - } -}; - -const setPlaybackRate = (currSpeed) => { - const id = getCurrentId(); - if (document.getElementById(`ytPlayback${id}`) === null) return false; - document.getElementById( - `ytPlayback${getCurrentId()}` - ).innerText = `${currSpeed}x`; - return true -}; - -function getVideo() { return document.querySelector("#shorts-player>div>video"); } - -//WheelProgram -function wheel(Element, codeA, codeB) { - Element.addEventListener("wheel", (event) => { - if (event.wheelDelta > 0) { - codeA(); - } else { - codeB(); - } - event.preventDefault(); - }, - { passive: false } - ); -} - -var injectedItem = new Set(); -var lastTime = -1; -var lastSpeed = 0; -var setSpeed = 1; - -const timer = setInterval(() => { - if (window.location.toString().indexOf("youtube.com/shorts/") < 0) return; - - const ytShorts = getVideo(); - var currentId = getCurrentId(); - var likeCount = getLikeCount(currentId); - var actionList = getActionElement(currentId); - var overlayList = getOverlayElement(currentId); - var autoplayEnabled = localStorage.getItem("yt-autoplay") === "true" ? true : false; - if (autoplayEnabled === null) autoplayEnabled = false; - - var progBarList = overlayList.children[2].children[0].children[0]; - progBarList.removeAttribute( "hidden" ) - - if ( topId < currentId ) - topId = currentId - - // video has to have been playing to skip. - // I'm undecided whether to use 0.5 or 1 for currentTime, as 1 isn't quite fast enough, but sometimes with 0.5, it skips a video above the minimum like count. - if (ytShorts && ytShorts.currentTime > 0.5 && ytShorts.duration > 1) { - - if (shouldSkipShort(currentId, likeCount)) { - console.log("[Better Youtube Shorts] :: Skipping short that had", likeCount, "likes"); - skippedId = currentId; - skipShort(ytShorts); - } - } - - if (injectedItem.has(currentId)) { - var currTime = Math.round(ytShorts.currentTime); - var currSpeed = ytShorts.playbackRate; - - if (autoplayEnabled && ytShorts && ytShorts.currentTime >= ytShorts.duration - 0.11) { - var nextButton = getNextButton(); - nextButton.click(); - } - - if (currTime !== lastTime) { - // Using this as a check whether the elements actually were injected on the page - var injectedSuccess = setTimer(currTime, Math.round(ytShorts.duration || 0)); - // If failed, retry injection during next interval - if (!injectedSuccess) injectedItem.delete(currentId); - lastTime = currTime; - } - if (currSpeed != lastSpeed) { - const setRateSuccess = setPlaybackRate(currSpeed); - if (setRateSuccess) lastSpeed = currSpeed; - } - - } else { - lastTime = -1; - lastSpeed = 0; - if (autoplayEnabled && ytShorts) ytShorts.loop = false; - - if (actionList) { - - const betterYTContainer = document.createElement("div"); - betterYTContainer.id = "betterYT-container"; - betterYTContainer.setAttribute("class", "button-container style-scope ytd-reel-player-overlay-renderer"); - - const ytdButtonRenderer = document.createElement("div"); - ytdButtonRenderer.setAttribute("class", "betterYT-renderer style-scope ytd-reel-player-overlay-renderer"); - - const ytButtonShape = document.createElement("div"); - ytButtonShape.setAttribute("class", "betterYT-button-shape"); - - const ytLabel = document.createElement("label"); - ytLabel.setAttribute("class", "yt-spec-button-shape-with-label"); - - const ytButton = document.createElement("button"); - ytButton.setAttribute("class", "yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-l yt-spec-button-shape-next--icon-button "); - // Playback Rate - var para0 = document.createElement("p"); - para0.classList.add("betterYT"); - para0.id = `ytPlayback${currentId}`; - - // Timer - const ytTimer = document.createElement("div"); - ytTimer.classList.add("yt-spec-button-shape-with-label__label"); - var span1 = document.createElement("span"); - span1.setAttribute("class", "yt-core-attributed-string yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--text-alignment-center yt-core-attributed-string--word-wrapping"); - span1.id = `ytTimer${currentId}`; - span1.setAttribute("role", "text"); - ytTimer.appendChild(span1); - - // Match YT's HTML structure - ytButton.appendChild(para0); - ytLabel.appendChild(ytButton); - ytLabel.appendChild(ytTimer); - ytButtonShape.appendChild(ytLabel); - ytdButtonRenderer.appendChild(ytButtonShape); - betterYTContainer.appendChild(ytdButtonRenderer); - - actionList.insertBefore(betterYTContainer, actionList.children[1]); - - // Autoplay Switch - const switchContainer = document.createElement("div"); - const autoplaySwitch = document.createElement("label"); - autoplaySwitch.classList.add("autoplay-switch"); - var checkBox = document.createElement("input"); - checkBox.type = "checkbox"; - checkBox.id = `autoplay-checkbox${currentId}`; - checkBox.checked = autoplayEnabled; - var autoplaySpan = document.createElement("span"); - autoplaySpan.classList.add("autoplay-slider"); - autoplaySwitch.append(checkBox, autoplaySpan); - switchContainer.appendChild(autoplaySwitch); - - actionList.insertBefore(switchContainer, actionList.children[1]); - - const autoplayTitle = document.createElement("div"); - autoplayTitle.classList.add("yt-spec-button-shape-with-label__label"); - var span2 = document.createElement("span"); - span2.setAttribute("class", "betterYT-auto yt-core-attributed-string yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--text-alignment-center"); - span2.setAttribute("role", "text"); - span2.textContent = "Autoplay"; - autoplayTitle.appendChild(span2); - - actionList.insertBefore(autoplayTitle, actionList.children[2]); - injectedItem.add(currentId); - - ytShorts.playbackRate = setSpeed; - setPlaybackRate(setSpeed); - injectedSuccess = setTimer(currTime || 0, Math.round(ytShorts.duration || 0)); - - betterYTContainer.addEventListener("click",() => { - ytShorts.playbackRate = 1; - setSpeed = ytShorts.playbackRate; - }); - - checkBox.addEventListener('change', () => { - if (checkBox.checked) { - localStorage.setItem("yt-autoplay", "true"); - ytShorts.loop = false; - } else { - localStorage.setItem("yt-autoplay", "false"); - ytShorts.loop = true; - } - }); - - wheel(ytButton, speedup, speeddown); - function speedup() { - if (ytShorts.playbackRate < 16) getVideo().playbackRate += 0.25; - setSpeed = getVideo().playbackRate; - } - function speeddown() { - if (ytShorts.playbackRate > 0.25) getVideo().playbackRate -= 0.25; - setSpeed = getVideo().playbackRate; - } - wheel(ytTimer, forward, backward); - function forward() { - getVideo().currentTime += 1; - } - function backward() { - getVideo().currentTime -= 1; - } - - } - // Progress bar - if (overlayList) { - var progBarList = overlayList.children[2].children[0].children[0]; - var progBarBG = progBarList.children[0]; - var progBarPlayed = progBarList.children[1]; // The red part of the progress bar - - const timestampTooltip = document.createElement("div"); - timestampTooltip.classList.add("betterYT-timestamp-tooltip"); - - progBarList.appendChild(timestampTooltip); - - // Styling to ensure rest of bottom overlay (shorts title/sub button) stay in place - overlayList.children[0].style.marginBottom = "-7px"; - progBarList.style.height = "10px"; - progBarList.style.paddingTop = "2px"; // Slight padding to increase hover box - - progBarList.classList.add('betterYT-progress-bar'); - progBarBG.classList.add('betterYT-progress-bar'); - progBarPlayed.classList.add('betterYT-progress-bar'); - - progBarList.addEventListener("mouseover", () => { - progBarBG.classList.add('betterYT-progress-bar-hover'); - progBarPlayed.classList.add('betterYT-progress-bar-hover'); - }); - progBarList.addEventListener("mousemove", (event) => { - let x = event.clientX - ytShorts.getBoundingClientRect().left; - // Deal with slight inaccuracies - if (x < 0) x = 0; - if (x > ytShorts.clientWidth) x = ytShorts.clientWidth; - // Get timestamp and round to nearest 0.1 - let timestamp = ((x / ytShorts.clientWidth) * ytShorts.duration).toFixed(1); - timestampTooltip.textContent = `${timestamp}s`; - // Ensure tooltip stays visible at edges of client - if ((x - (timestampTooltip.offsetWidth / 2)) > (ytShorts.clientWidth - timestampTooltip.offsetWidth)) { - timestampTooltip.style.left = `${ytShorts.clientWidth - timestampTooltip.offsetWidth}px`; - } else if ((x - (timestampTooltip.offsetWidth / 2)) <= 0) { - timestampTooltip.style.left = "0px"; - } else { - timestampTooltip.style.left = `${x - (timestampTooltip.offsetWidth / 2)}px`; - } - timestampTooltip.style.top = "-20px"; - timestampTooltip.style.display = 'block'; - }); - progBarList.addEventListener("mouseout", () => { - progBarBG.classList.remove('betterYT-progress-bar-hover'); - progBarPlayed.classList.remove('betterYT-progress-bar-hover'); - timestampTooltip.style.display = 'none'; - }); - progBarList.addEventListener("click", (event) => { - let x = event.clientX - ytShorts.getBoundingClientRect().left; - if (x < 0) x = 0; - if (x > ytShorts.clientWidth) x = ytShorts.clientWidth; - ytShorts.currentTime = (x / ytShorts.clientWidth) * ytShorts.duration; - }); - } - if (currentId !== null) setVolumeSlider(ytShorts, currentId); - } - if (ytShorts) checkVolume(ytShorts); -}, 100); - -/** - * Converts a formatted number to its full integer value. - * @param {string} string value to be converted (eg: 1.4M, 1,291 or 727) - * @returns converted number - */ -function convertLocaleNumber( string ) -{ - if ( typeof string !== "string" ) return - - // todo - add formats from other langs - const multipliers = { - // English - "b": 1_000_000_000, - "m": 1_000_000, - "k": 1_000, - - // Italian - "mln": 1_000_000, - - // Indian English - "lakh": 100_000, - - // Portuguese - "mil": 1_000, - - // Spanish - "mil": 1_000, - - // French - "mio": 1_000_000, - "md": 1_000, - - // German - "mio": 1_000_000, - "mrd": 1_000_000_000, - "tsd": 1_000, - - // Japanese - "億": 1_000_000_000, - "万": 10_000, - - // Chinese (Simplified) - "亿": 1_000_000_000, - "万": 10_000, - - // Chinese (Traditional) - "億": 1_000_000_000, - "萬": 10_000, - - // Russian - "млн": 1_000_000, - "тыс": 1_000, - - // Hindi - "करोड़": 10_000_000, - "लाख": 100_000, - - // Arabic - "مليون": 1_000_000, - "مليار": 1_000_000_000, - "ألف": 1_000, - - // Korean - "억": 100_000_000, - "만": 10_000, - - // Turkish - "milyon": 1_000_000, - "milyar": 1_000_000_000, - "bin": 1_000, - - // Vietnamese - "triệu": 1_000_000, - "tỷ": 1_000_000_000, - "nghìn": 1_000, - - // Thai - "ล้าน": 1_000_000, - "พันล้าน": 1_000_000_000, - "พัน": 1_000, - - // Dutch - "mio": 1_000_000, - "mld": 1_000_000_000, - "k": 1_000, - - // Greek - "εκ": 1_000_000, - "δισ": 1_000_000_000, - "χιλ": 1_000, - - // Swedish - "mn": 1_000_000, - "md": 1_000_000_000, - "t": 1_000, - } - const regex = /^(\d{1,3}(?:(?:,\d{3})*(?:\.\d+)?)|(?:\d+))(?:([,.])(\d+))?([a-z]*)\.?$/i; - const matches = string.match(regex); - - if (!matches) { - return 0; - } - - let numericPart = matches[1].replace(/,/g, ""); // Remove commas - if (matches[2] && matches[3]) { - // Decimal part exists, add it back - numericPart += `.${matches[3]}`; - } - - const multiplier = matches[4].toLowerCase(); - const hasMultiplier = Object.keys(multipliers).includes(multiplier); - - if (hasMultiplier) { - return numericPart * multipliers[multiplier]; - } else { - // Remove decimals and commas from the numeric part - const numericValue = parseInt(numericPart.replace(/[.,]/g, ""), 10); - return numericValue; - } -} - -function goToNextShort( short ) -{ - const scrollAmount = short.clientHeight - document.getElementById( "shorts-container" ).scrollTop += scrollAmount -} - -function goToPrevShort( short ) -{ - const scrollAmount = short.clientHeight - document.getElementById( "shorts-container" ).scrollTop -= scrollAmount -} diff --git a/icons/byts128.png b/icons/byts128.png deleted file mode 100644 index 0d0927eaba66f0710d582541ad204d9212f78daf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2723 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 diff --git a/icons/byts16.png b/icons/byts16.png deleted file mode 100644 index 92604f368542686491dadd5cd1a7845200e37325..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 603 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 d)}{(U^gqa3GT3K-%&h + + + + + + Better YT Shorts + + +
+ + + diff --git a/manifest.json b/manifest.json index a751c4b..790f7d1 100644 --- a/manifest.json +++ b/manifest.json @@ -1,26 +1,30 @@ { - "name": "Better YouTube Shorts", - "description": "Take back the controls on YouTube Shorts with playback, volume, progress bar and more.", - "version": "2.6.0", - "manifest_version": 3, - "permissions": ["storage"], - "action": { - "default_popup": "popup.html" - }, - "background" : { - "service_worker": "background.js" - }, - "content_scripts": [ - { - "matches": ["https://*.youtube.com/*"], - "js": ["content-script.js"], - "css": ["styles.css"] - } - ], - "icons": { - "16": "icons/byts16.png", - "32": "icons/byts32.png", - "48": "icons/byts48.png", - "128": "icons/byts128.png" + "manifest_version": 3, + "name": "Better YouTube Shorts (BETA)", + "description": "Take back the controls on YouTube Shorts with playback, volume, progress bar and more.", + "version": "0.0.0", + "action": { "default_popup": "index.html" }, + "permissions": ["storage"], + + "content_scripts": [ + { + "js": [ + "src/content.ts" + ], + "matches": [ + "https://*.youtube.com/*" + ] } -} \ No newline at end of file + ], + + "icons": { + "16": "src/assets/icons/bys-16.png", + "32": "src/assets/icons/bys-32.png", + "48": "src/assets/icons/bys-48.png", + "128": "src/assets/icons/bys-128.png" + }, + + "background": { + "service_worker": "src/background.ts" + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..529ceca --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2109 @@ +{ + "name": "better-yt-shorts", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "better-yt-shorts", + "version": "0.0.0", + "dependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "devDependencies": { + "@crxjs/vite-plugin": "^1.0.14", + "@types/chrome": "^0.0.243", + "@types/firefox-webext-browser": "^111.0.1", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "@vitejs/plugin-react": "^1.3.0", + "typescript": "^4.6.3", + "vite": "^2.9.15" + } + }, + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", + "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.8", + "@babel/types": "^7.22.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", + "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5", + "@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.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", + "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "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-module-imports": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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-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-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.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "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-react-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz", + "integrity": "sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", + "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "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-react-jsx-source": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", + "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "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/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@crxjs/vite-plugin": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@crxjs/vite-plugin/-/vite-plugin-1.0.14.tgz", + "integrity": "sha512-emOueVCqFRFmpcfT80Xsm4mfuFw9VSp5GY4eh5qeLDeiP81g0hddlobVQCo0pE2ZvNnWbyhLrXEYAaMAXjNL6A==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^4.1.2", + "@webcomponents/custom-elements": "^1.5.0", + "acorn-walk": "^8.2.0", + "cheerio": "^1.0.0-rc.10", + "connect-injector": "^0.4.4", + "debug": "^4.3.3", + "es-module-lexer": "^0.10.0", + "fast-glob": "^3.2.11", + "fs-extra": "^10.0.1", + "jsesc": "^3.0.2", + "magic-string": "^0.26.0", + "picocolors": "^1.0.0", + "react-refresh": "^0.13.0", + "rollup": "^2.70.2" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "@vitejs/plugin-react": ">=1.2.0" + }, + "peerDependencies": { + "vite": "^2.9.0" + } + }, + "node_modules/@crxjs/vite-plugin/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", + "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "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.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "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==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "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/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@types/chrome": { + "version": "0.0.243", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.243.tgz", + "integrity": "sha512-4PHv0kxxxpZFHWPBiJJ9TWH8kbx0567j1b2djnhpJjpiSGNI7UKkz7dSEECBtQ0B3N5nQTMwSB/5IopkWGAbEA==", + "dev": true, + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@types/filesystem": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz", + "integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==", + "dev": true, + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz", + "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==", + "dev": true + }, + "node_modules/@types/firefox-webext-browser": { + "version": "111.0.1", + "resolved": "https://registry.npmjs.org/@types/firefox-webext-browser/-/firefox-webext-browser-111.0.1.tgz", + "integrity": "sha512-mmHWdQTCT68X0hh0URrsIyWhJeFzZHaiprj6nni/CmsAmqYq27T0eZyu1ePeKJ/zuDD3wqtTzm5TwRFAso+oPw==", + "dev": true + }, + "node_modules/@types/har-format": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.11.tgz", + "integrity": "sha512-T232/TneofqK30AD1LRrrf8KnjLvzrjWDp7eWST5KoiSzrBfRsLrWDPk4STQPW4NZG6v2MltnduBVmakbZOBIQ==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.18.tgz", + "integrity": "sha512-da4NTSeBv/P34xoZPhtcLkmZuJ+oYaCxHmyHzwaDQo9RQPBeXV+06gEk2FpqEcsX9XrnNLvRpVh6bdavDSjtiQ==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz", + "integrity": "sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.10", + "@babel/plugin-transform-react-jsx": "^7.17.3", + "@babel/plugin-transform-react-jsx-development": "^7.16.7", + "@babel/plugin-transform-react-jsx-self": "^7.16.7", + "@babel/plugin-transform-react-jsx-source": "^7.16.7", + "@rollup/pluginutils": "^4.2.1", + "react-refresh": "^0.13.0", + "resolve": "^1.22.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@webcomponents/custom-elements": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", + "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==", + "dev": true + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "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/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "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.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "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.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001518", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001518.tgz", + "integrity": "sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA==", + "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/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "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/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/connect-injector": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/connect-injector/-/connect-injector-0.4.4.tgz", + "integrity": "sha512-hdBG8nXop42y2gWCqOV8y1O3uVk4cIU+SoxLCPyCUKRImyPiScoNiSulpHjoktRU1BdI0UzoUdxUa87thrcmHw==", + "dev": true, + "dependencies": { + "debug": "^2.0.0", + "q": "^1.0.1", + "stream-buffers": "^0.2.3", + "uberproto": "^1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/connect-injector/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect-injector/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "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/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.482", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.482.tgz", + "integrity": "sha512-h+UqpfmEr1Qkk0zp7ej/jid7CXoq4m4QzW6wNTb0ELJ/BZCpA4wgUylBIMGCe621tnr4l5VmoHjdoSx2lbnNJA==", + "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==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.10.5.tgz", + "integrity": "sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", + "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.14.54", + "esbuild-android-64": "0.14.54", + "esbuild-android-arm64": "0.14.54", + "esbuild-darwin-64": "0.14.54", + "esbuild-darwin-arm64": "0.14.54", + "esbuild-freebsd-64": "0.14.54", + "esbuild-freebsd-arm64": "0.14.54", + "esbuild-linux-32": "0.14.54", + "esbuild-linux-64": "0.14.54", + "esbuild-linux-arm": "0.14.54", + "esbuild-linux-arm64": "0.14.54", + "esbuild-linux-mips64le": "0.14.54", + "esbuild-linux-ppc64le": "0.14.54", + "esbuild-linux-riscv64": "0.14.54", + "esbuild-linux-s390x": "0.14.54", + "esbuild-netbsd-64": "0.14.54", + "esbuild-openbsd-64": "0.14.54", + "esbuild-sunos-64": "0.14.54", + "esbuild-windows-32": "0.14.54", + "esbuild-windows-64": "0.14.54", + "esbuild-windows-arm64": "0.14.54" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", + "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", + "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", + "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", + "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", + "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", + "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", + "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", + "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", + "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", + "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", + "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", + "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", + "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", + "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", + "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", + "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", + "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", + "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", + "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", + "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "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/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "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/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "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.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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-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/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/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "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/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "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-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/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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/magic-string": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", + "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "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/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/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "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.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "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/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "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/postcss": { + "version": "8.4.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", + "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "dev": true, + "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.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "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/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-refresh": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.13.0.tgz", + "integrity": "sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "dependencies": { + "is-core-module": "^2.11.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": "2.77.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz", + "integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "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/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "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/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, + "node_modules/stream-buffers": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-0.2.6.tgz", + "integrity": "sha512-ZRpmWyuCdg0TtNKk8bEqvm13oQvXMmzXDsfD4cBgcx5LouborvU5pm3JMkdTP3HcszyUI08AM1dHMXA5r2g6Sg==", + "dev": true, + "engines": { + "node": ">= 0.3.0" + } + }, + "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/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/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uberproto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/uberproto/-/uberproto-1.2.0.tgz", + "integrity": "sha512-pGtPAQmLwh+R9w81WVHzui1FfedpQWQpiaIIfPCwhtsBez4q6DYbJFfyXPVHPUTNFnedAvNEnkoFiLuhXIR94w==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "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/vite": { + "version": "2.9.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.16.tgz", + "integrity": "sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA==", + "dev": true, + "dependencies": { + "esbuild": "^0.14.27", + "postcss": "^8.4.13", + "resolve": "^1.22.0", + "rollup": ">=2.59.0 <2.78.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": ">=12.2.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "less": "*", + "sass": "*", + "stylus": "*" + }, + "peerDependenciesMeta": { + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4364957 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "better-yt-shorts", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "devDependencies": { + "@crxjs/vite-plugin": "^1.0.14", + "@types/chrome": "^0.0.243", + "@types/firefox-webext-browser": "^111.0.1", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "@vitejs/plugin-react": "^1.3.0", + "typescript": "^4.6.3", + "vite": "^2.9.15" + } +} diff --git a/popup.html b/popup.html deleted file mode 100644 index 039b2ff..0000000 --- a/popup.html +++ /dev/null @@ -1,48 +0,0 @@ - - - -
-
-
-
Better Youtube Shorts
-
v
-
- logo -
-
- -

Keybinds

- -
-

Extra Options

-
-
- - -
-
- - -
-
- - -
Reset keybindsGitHub
- - - - -
\ No newline at end of file diff --git a/popup.js b/popup.js deleted file mode 100644 index c30d316..0000000 --- a/popup.js +++ /dev/null @@ -1,280 +0,0 @@ -const browserObj = (typeof browser === 'undefined') ? chrome : browser; -const version = browserObj.runtime.getManifest().version; -document.getElementById('version').textContent = version; - -const modal = document.getElementById("edit-modal"); -const resetBtn = document.querySelector(".reset-btn"); -const editBtnList = document.querySelectorAll(".edit-btn"); -const closeBtn = document.querySelector(".close-btn"); -const keybindInput = document.getElementById("keybind-input"); -const keybindTable = document.getElementById( "keybind-table" ) - -let modalTitleSpan = document.getElementById("modal-title-span"); -let invalidKeybinds = [ - 'Backspace', - 'Enter', - 'NumpadEnter', - 'Escape', - 'Tab', - 'Space', - 'PageUp', - 'PageDown', - 'ArrowUp', - 'ArrowDown', - 'F13', // printscreen - 'MetaLeft', // windows/command - 'MetaRight', - - 'ControlLeft', - 'ControlRight', - 'ShiftLeft', - 'ShiftRight', - 'AltLeft', - 'AltRight', -]; - -const defaultKeybinds = { - 'Seek Backward': 'ArrowLeft', - 'Seek Forward': 'ArrowRight', - 'Decrease Speed': 'KeyU', - 'Reset Speed': 'KeyI', - 'Increase Speed': 'KeyO', - 'Decrease Volume': 'Minus', - 'Increase Volume': 'Equal', - 'Toggle Mute': 'KeyM', - 'Next Frame': 'Comma', - 'Previous Frame': 'Period', - 'Next Short': 'KeyS', - 'Previous Short': 'KeyW', -}; -const defaultExtraOptions = { - skip_enabled: false, - skip_threshold: 500, -} - -// this is so that the bindings are always generated in the right order -const bindsOrder = [ - 'Seek Backward', - 'Seek Forward', - 'Decrease Speed', - 'Reset Speed', - 'Increase Speed', - 'Decrease Volume', - 'Increase Volume', - 'Toggle Mute', - 'Next Frame', - 'Previous Frame', - 'Next Short', - 'Previous Short', -] - -let currentKeybinds = Object.assign({}, defaultKeybinds); -let currentExtraOpts = Object.assign({}, defaultExtraOptions); -let currentKeybindArray = []; -let keybindState = ''; - -// Set user's prefers-color-scheme -if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.setAttribute('data-theme', "dark"); -} -window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { - const newColorScheme = event.matches ? "dark" : "light"; - document.documentElement.setAttribute('data-theme', newColorScheme); -}); - -// Get keybinds from storage -browserObj.storage.local.get(['keybinds']) -.then((result) => { - let updatedkeybinds = result['keybinds']; - if (updatedkeybinds) { - // Set default keybinds if not exists - for (const [cmd, keybind] of Object.entries(defaultKeybinds)) { - if (!result.keybinds[cmd]) result.keybinds[cmd] = keybind; - } - - populateKeybindsTable( updatedkeybinds ) - currentKeybinds = updatedkeybinds; - } - // Keybind array for easier checking if keybind is already in use - currentKeybindArray = Object.values(currentKeybinds); -}); - -// Get extra options from storage -browserObj.storage.local.get(['extraopts']) - .then((result) => { - let updatedExtraOpts = result['extraopts']; - if ( updatedExtraOpts ) { - // Set default extraopts if not exists - for (const [ option, value ] of Object.entries( defaultExtraOptions )) { - if (result.extraopts[ option ]) continue - - result.extraopts[ option ] = value; - } - } - - if (result.extraopts) { - // set skip toggle - document.getElementById( "extra_options_skip_enabled" ).checked = result.extraopts.skip_enabled; - - // set skip threshold - document.getElementById( "extra_options_skip_threshold" ).value = result.extraopts.skip_threshold; - } - - }) - -// Open modal -for (let i = 0; i < editBtnList.length; i++) { - editBtnList[i].onclick = function() { - modal.style.display = "block"; - keybindInput.focus(); - keybindInput.select(); - modalTitleSpan.textContent = this.id; - keybindState = this.id; - } -} - -resetBtn.onclick = () => { - currentKeybinds = Object.assign( {}, defaultKeybinds ); - currentKeybindArray = Object.values(currentKeybinds); - - populateKeybindsTable( defaultKeybinds ) - - browserObj.storage.local.set({ 'keybinds' : defaultKeybinds }); -} - -// Close modal (x) -closeBtn.onclick = () => { - modal.style.display = "none"; -} -// Close modal (click outside) -window.onclick = (event) => { - if (event.target == modal) modal.style.display = "none"; -} - -keybindInput.addEventListener('keydown', (event) => { - event.preventDefault(); - var keybind = event.code; - var keybindAlt = event.key; // for legacy, will become obsolete - - console.log( `[BYS] :: Attempting to set key: ${keybind}` ) - - if ( invalidKeybinds.includes(keybind) ) { - keybindInput.value = ""; - closeBtn.click(); - alert("Invalid keybind: <<" + keybind + ">> is not allowed."); - return; - } - if ( currentKeybindArray.includes( keybind ) || currentKeybindArray.includes( keybindAlt ) ) { - keybindInput.value = ""; - closeBtn.click(); - alert("Invalid keybind: <<" + keybind + ">> is already in use."); - return; - } - - // document.getElementById(keybindState+'-span').textContent = keybind; - currentKeybinds[keybindState] = keybind; - currentKeybindArray = Object.values(currentKeybinds); - - browserObj.storage.local.set({ 'keybinds' : currentKeybinds }) - .then(() => { - keybindInput.value = ""; - closeBtn.click(); - }); - - populateKeybindsTable( currentKeybinds ) -}); - -document.getElementById( "extra_options_skip_enabled" ).addEventListener( "change", e => { - browserObj.storage.local.get(['extraopts']).then( result => { - if (result !== null && Object.keys(result).length !== 0) currentExtraOpts = result.extraopts; - currentExtraOpts.skip_enabled = e.target.checked; - browserObj.storage.local.set({ 'extraopts' : currentExtraOpts }); - }); -}); - -document.getElementById( "extra_options_skip_threshold" ).addEventListener( "input", e => { - browserObj.storage.local.get(['extraopts']).then( result => { - if (result !== null && Object.keys(result).length !== 0) currentExtraOpts = result.extraopts - currentExtraOpts.skip_threshold = e.target.valueAsNumber; - browserObj.storage.local.set({ 'extraopts' : currentExtraOpts }); - }); -}); - -const EDIT_BUTTON_SVG_STRING = ` - - - - - - - - -` - -function populateKeybindsTable( keybinds ) -{ - keybindTable.innerHTML = ` - - Command - Key - - ` - - for ( const command of bindsOrder ) - { - const bind = keybinds[ command ] - const row = generateKeybindItem( command, bind ) - - if ( row === null ) continue - - keybindTable.appendChild( row ) - } -} - -/** - * Creates a table row for the keybinds table. - * @param {string} command - * @param {string} bind - * @returns Table Row Element - */ -function generateKeybindItem( command, bind ) -{ - const tr = document.createElement( "tr" ) - - // The command name - const td1 = document.createElement( "td" ) - td1.innerHTML = command - - // the key that is bound - const td2 = document.createElement( "td" ) - td2.innerHTML = ` -
- ${bind} -
- ` - - // the rebind icon - const td3 = document.createElement( "td" ) - td3.innerHTML = ` - - ` - td3.querySelector( `[id="${command}"]` ).addEventListener( "click", e => { - modal.style.display = "block"; - - keybindInput.focus(); - keybindInput.select(); - - modalTitleSpan.innerText = command; - keybindState = command; - - } ) - - tr.appendChild( td1 ) - tr.appendChild( td2 ) - tr.appendChild( td3 ) - - return tr -} diff --git a/src/assets/icons/bys-128.png b/src/assets/icons/bys-128.png new file mode 100644 index 0000000000000000000000000000000000000000..ac60f11b497c60d308a3d1f3191512afe4555007 GIT binary patch literal 8601 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 QGW{<`E=!UgTC%p_7qehr&Hw-a literal 0 HcmV?d00001 diff --git a/src/assets/icons/bys-32.png b/src/assets/icons/bys-32.png new file mode 100644 index 0000000000000000000000000000000000000000..2df486a35fe1fe47d8d88ab8bcc62d436bf2715c GIT binary patch literal 3003 zcma)8dpuO>8z19R=C?&Dwymwxaj8aQZn;gyB?co}M8;NXnmKbZmosJ#h6-t?3w9%H zB}J*Ql}kxP_=Rc{%1UK7i%ny@P${;eerHCh)@S$g`{T^a`+mRA_kEt{eV*scZvRzY z({NwmFc{1elmARAoPxvL#ty~1#x0U97itb;*4`r(og{j zgs=gnL@ZU%lyp$5OGAHawuvC1r63|Y$nx_C++jHc*xK0IkU(b~;3VhrXn`J{?~TzF z9TXslj7B6zM@QR4Q*B^5pGbCebR?1}L<)s~G6;$oDZ*9~qzZG5#JGkBq~OSfGDHYV z0gWbm6C8=qK@i1(51vV66XH_E`{>a;5S45hk!(XEeoB@JU<6hO;D0cgfF~J3O5w*C zK08K(Orj$~-apVa$Rs+NYd^n#k3u5(ED#04h(Z(bKB=EM^dW9VP>c*B20{urQqF-G zQIHfd*X9B}jpimqEHDW2Lg#2L?Y8{9cdJjHHl0jO_2Ryu8#LQC^sJ^EKqyaRqO0Wi0SU`FNb+T zF^VYyy;cBBhPxfv(aw%Qu_0@@ntP-93zd+VfDX#!FgKC|akWW6<;GD3 z%tNBta>$L3)({X~&FcHSPFO_?AXM?wtNgrPi0{24Y8v~4 zdrrhj0XS(1pG*SOZhQ}&FzBcvPRuHFF){5RDOyKzbe8$Ge0v&$(Mb@Z6)~w2Fqp+@ zk!h{g9s#qixbOr9=1D45qs1xXJgY!c>Z@SZ^_1r#>d6~gRTL3&YO$s>=wG&y3$i8*7mK>RpWSEhx<8Mho5bp|E$;G zbzzF=NAmoylLF+`_xw*h4&He!xO4FL0D4Z@vc*F0vWUrcA=DQ{A zFIL6946Ld+^RVnMW<+O?Q6q2ZOoNa^B`#yhCbg`l{TE8AvrXw$1!L`bVZAFKSmkXx z2-&h{u=g9sdCsx$O_jdp8nwA4ta1zhYaT6+ORR1PKQnhJ=ZtIC7y6xy^%;+urAuz6 zQ_e0u`S#tDlbH_<9qw*jSfDI{tq;~MvVYN=hV9(N!l-5DF!IS-u7^fk# z@Q|MS)diO2-N>}B22(2Ly!)~J_o~1pO6SBZVL|P8KZWM=T}I}0tq?P69%9R*bNxm| z>Kcsu7{9}o5gUWPlMH3vp$ZJf#$5C0D37~DVK90%Ob@ppmg@ArrsR3Svo6Q0yJBDD z@pd@P<{&d0^t^7UHm0O9b{Qal_>jQ8Dr@@y``$Hr>!-doTnOx~*rh{G(lMK1WrMJd zEesYIT3JO<(vMxBu3sOyC;6wM!lHqnuQ$xA2pYNAI94^ zNxyh_6QoareNXW6hqCSh|0j z({<0LME8>~j$d7wmYw*W()U!B>*Jl%s84Fr{iOSR2FTcOyeqy^bgN?8g}Rakv9W5p zbyVUMWBW@4p4YJTHjg4Aa3QGY1jWF@Ak$%%s<-yOMdP+A?(|}BVA#FYdGFW>&#Ww5 z(u0FvK2Ig83+MRS&`AI;G1sh5T$Y->HZ8%o|5tolyR%~jROmQm;R1ExnhrvDlBr(5 z;Bm_xeh9YJupimuYft$T|@m~J)i*+Nh9lx4cGT~r7K}mWUhGGlvbb?-G z>`E~CZD}h*F}6D9o1&IuZ&KsG-q2AvtL2OK=5|lV{5+BBbwvH!qw(iP%8OehIuDmP zfS|pX`3>kt!?3HWbqdp$*tJFK$Ukkffw@mo4ooLjibD1r@H@DnkUci_dWYec(M{Es zK6h#6I5nQ*tq%zZAxzyc!SVC{Q#N$?wm&-b3#-K^jXwvQ8PH8*E_!9+b}RP)A%SqHOxL6d_%{Zj>E_FN_0odXVT_Ag4lTTKU&Uo$dVtVPT6c6@UU zJbCSj>!X!@x)F`vUs-b5kk;xoaOObvF0(yeLb$oxQO5y~?;Byf{J|xa9g$JbtpoGV zI@*=NmV=(c;&}Ty-6q)IZp+<*d8ZE-_07I=KW8n&Z1j(r!(L-ut9>=k_SM$}0I{$*t0!H|tFf5BMmL oi!K$@?6x?U_uScjKkQCCc`JKvacj&N$4rr&>zGN|hIxj;9A<_Yq(rGjMdT`1Br;m7 zlFhYhD4sc9GDN_ zN2O!nf~q<=j7rA9J&hfZ4on-spK2S<0)7g2bn^`l^xZ**V=ctcJQS#a25?9)9xaH@ zM)5H4RlO+i_sTH>4qK&g0x@ucgEP#A!2)2$dd7N4xP=%D%_37!uDG3_g~1&L?$6;c zQ3wQ=%hlr=>M>Y;2>l&9b|8=j2m=FMkfF;CqjN|+T{>H1Ma3r_IDqZTqB1#D1|7Dd zljOq);b7o!5QqKc8IAcxIi3AEdN27@;O_(h9`$Ps ze>1a!d`IU{DgQ!WLB6Adxpr{)?IMhMFn zz=r~Kj>c*(Kx-%)7C_V($Ed5k22nYGC=Ae^^wRgWCsSBN(uX( zY=qJ`LKzu@WYo88usp~l4(b2le@QrMh5gAXur&YEqpvbHj35RJbPkNcaMjtbke!W< zGmAl?27wscb(b~F4sTzZe*Xkk?k6Z@rNG_kRPaU(R_(9K5WZqy zfJh|b8#~1Bv6ZHQp;t=@WlLpq7_6|>qfedm6A=7q_h}nMU2P#4Y_*V3B;OSeFmNV| zK@RZ+$g56(>OP@t28F{Vu>dPSu!b;jD++}Q`WglUyOZii2Usuz7?{=1dHo{A^#?$W z-(Kb4>jm-IJH$$3|K%PU@m&DuRl+xu0F?W522L1oR3X01Dsb~<+5vR1j#%I<8;uY) zg+PR&s9;5WR|yb^R#HgPLmiyU#(P+bzo2UU$XquHgS79^NwDW;@h9iOHF-89v{FQP}qzztC%Nf9|jt zg={vElbQWCLiKH*=v;9^V5+|ArZ^W?b(eGDYxm>#-8%$5E|{EaMp{#1lK!zoDOG7z zuWdiZnI2QVw&$St456|sMfljSXS10zcEKI5B=1nR%ZPerm0=g-W$Oq{={L)&&&y+~ ziWb}Qz5DEkw&eSq1B^-1q;#o6I~6zYNu|u-SFg^8ew?72s8%+& zWJR*;dLrsRcCuS6UZIFec=2|+&UzxVDf4UCDDL#DrXVtzjnOqNCz zdnw`bUUQrpFtZAHJo5lV6n+RNWUjHbOAsBAtkbpZ-Nak3nb_UQbMDDnbW0@{L-$v0 zTqBmdq!bmQK7$*_3Eouu>K(PbDO0mw5L%aHTD3`&V0gK`8+jp0OC$->IaPn$>ryy2 z!|!zGd3D+aN`t@tD zRdDmyxxj&%3rR-?qX#V|D)T7WSXI-~=1k{{A zHP>7+?f0`H;bZ{zXs$Zh>6q6UN7F2khiy8Edb@61>5@}@!fm(5mHS}I`2LkX@{NHH z9vV0X{z0+IP$lQvp zjjy}X>bZC^DFJ~WxrhZ)9k)9U6QMn>zttxrHfqM>*+TL3j+IH!&E|?(A zoA%tnx>Wg>;t06{h_}S~G+4o-Swipa$ffz|;l-E>hsdBxao?w2U5EjBi65hAAug`VCVBp{~hO zxi-CPZ$)L|3LnpHIV01v71+~uad_#n+Icy&Wx7eeIk{w0S#yknWNiWhAHR;t1^-%hu6~VK}Vm9W#%%CfV9V){pSV1NGv}!`e;<44S$GF&}SuL?Bw$ zsKN?HCvO)Yu2Tc{=2*lRK-SH@y!`B-! z<1*kE*uXKlBb&$hRa0|XLy^}F3oK?JiWw}qF!A*cIi zpa~6u!-*+RGkc6E2D{#%VbSBH7GIZ54{SgZXOB-_7vAdUQqk>*a?2XM{LjPdxPdiek zzph8!Fn%j~`F5r8#skW^YfxD{`I$#g*B+GFInvE-HG4m52Qj^R{GhTQ_Jt+vWM72Q zD=l>$iZ?c7okOx9GwP_VZ|i{WUC{+XmEvLai(6{aKVLJbf7cp=%$n!8x>Z&uEHR)R z!3*govw4HjMCmPF^hKqXqK+0EqF|9dfZCfOD|ljCOKDMqUXfQg>#FGaQc+Xe58w1F zHU_@c<5bF9=aBBUdZ%%VadNpzxZ&`~Rax8HytEUO6`cyDl;Vj5oxA~w@#zMqC1v|x zVJ=4D?_pY#bRvaNf zYFo!XcwO7fSSuT$77ahIMv{zgIK4%G!<}IBnfj)aywXAI4L22ED3LMzND<-E*4=?G zvEkHrwF7O9?lEg*-;_EDOWslCtp^^%)P#*o3hs}^M47F{*u3MLy%4T{cTg8wBB|;5 z?DVbK2W1X``d#rmQQj8)<@^U8^--N^>-S50oG|>e3dudSm9P7=UHSAu^@f^aZ6}Gg zk>iHN;i|}bfxTm${~+}3Bi)dPgO>(lpIw|xJHx-~{6<7*CRi@60KDkmuysu`A=YivTSb0(1+wYW`4j_bUB4FQO{tD|uJ-Kw}C+d0~5_`SbT`TjH-R3q3Ixzv0erU-=6PvBNv! Ju2>(4{vT`6I*R}R literal 0 HcmV?d00001 diff --git a/src/background.ts b/src/background.ts new file mode 100644 index 0000000..953c594 --- /dev/null +++ b/src/background.ts @@ -0,0 +1,3 @@ +import BROWSER from "./background/browser"; + +BROWSER.runtime.setUninstallURL( "https://github.com/ynshung/better-yt-shorts/blob/master/UNINSTALL.md" ); diff --git a/src/background/browser.ts b/src/background/browser.ts new file mode 100644 index 0000000..6ece709 --- /dev/null +++ b/src/background/browser.ts @@ -0,0 +1,2 @@ +const BROWSER = ( typeof browser === 'undefined' ) ? chrome : browser +export default BROWSER \ No newline at end of file diff --git a/src/background/handleColorScheme.ts b/src/background/handleColorScheme.ts new file mode 100644 index 0000000..1085b9a --- /dev/null +++ b/src/background/handleColorScheme.ts @@ -0,0 +1,28 @@ +import BROWSER from "./browser" + +export function handleColorScheme( isDarkScheme: boolean ) +{ + console.log( `[BYS] :: processing scheme (recognised as ${ isDarkScheme ? "DARK" : "LIGHT" })` ) + + // todo - create dark/light icons and change these here. + if ( isDarkScheme ) + BROWSER.browserAction.setIcon({ + path: { + "128": `bys-128.png`, + "48": `bys-48.png`, + "32": `bys-32.png`, + "16": `bys-16.png`, + } + }) + + else + BROWSER.browserAction.setIcon({ + path: { + "128": `bys-128.png`, + "48": `bys-48.png`, + "32": `bys-32.png`, + "16": `bys-16.png`, + } + }) + +} \ No newline at end of file diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx new file mode 100644 index 0000000..672d6e0 --- /dev/null +++ b/src/components/Popup.tsx @@ -0,0 +1,58 @@ +import "../css/popup.css" +import bys_logo from "../assets/icons/bys-48.png" +import { VERSION } from "../lib/declarations" + +// todo - split this into its component parts + +function Popup() { + return ( + <> +
+
+
+
+
Better Youtube Shorts
+
v{VERSION}
+
+ logo +
+
+

Keybinds

+ +
+

Extra Options

+
+
+ + +
+
+ + +
+
+
Reload the page for changes to take effect.
+
Reset keybindsGitHub
+ {/* Modal */} +
+
+
+
Edit keybind:
+ × +
+
+
+ + +
Does not support key combinations
+
+
+
+
+
+ + + ) +} + +export default Popup diff --git a/src/content.ts b/src/content.ts new file mode 100644 index 0000000..3204a12 --- /dev/null +++ b/src/content.ts @@ -0,0 +1,476 @@ +import { handleColorScheme } from "./background/handleColorScheme" +import { goToNextShort, goToPrevShort } from "./lib/changeShort" +import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, keybinds, setKeybinds, state, storage } from "./lib/declarations" +import { getActionElement, getCurrentId, getLikeCount, getNextButton, getOverlayElement, getVideo, getVolumeContainer } from "./lib/getters" +import { shouldSkipShort, skipShort } from "./lib/skipShort" +import { wheel } from "./lib/utils" + +/** + * content.ts + * + * Code in this file will be injected into the page itself. + * For popup code, see ./main.tsx + */ + +// watch for color scheme changes +window.matchMedia( "(prefers-color-scheme: dark)" ).addEventListener( "change", ({matches}) => handleColorScheme( matches ) ) + + +// Using localStorage as a fallback for browser/chrome.storage.local +setKeybinds( JSON.parse( localStorage.getItem("yt-keybinds") as string ) ) + +storage.get(["keybinds"]) +.then((result) => { + if (result.keybinds) { + // Set default keybinds if not exists + for (const [cmd, keybind] of Object.entries( DEFAULT_KEYBINDS )) { + if (!result.keybinds[cmd]) result.keybinds[cmd] = keybind + } + if (result.keybinds !== keybinds) localStorage.setItem("yt-keybinds", JSON.stringify(result.keybinds)) + setKeybinds( result.keybinds ) + } +}) + +var extraOptions = JSON.parse( localStorage.getItem("yt-extraopts") as string ) +storage.get( ["extraopts"] ) + .then((result) => { + if (result.extraopts) + { + // Set default options if not exists + for ( const [ option, value ] of Object.entries( DEFAULT_OPTIONS ) ) { + if ( result.extraopts[ option ] ) continue + result.extraopts[ option ] = value + } + + if ( result.extraopts !== extraOptions ) + localStorage.setItem("yt-extraopts", JSON.stringify(result.extraopts) ) + + extraOptions = result.extraopts + } + }) + +document.addEventListener("keydown", (data) => { + if ( + document.activeElement === document.querySelector(`input`) || + document.activeElement === document.querySelector("#contenteditable-root") + ) return // Avoids using keys while the user interacts with any input, like search and comment. + const ytShorts = getVideo() + if (!ytShorts) return + if (!keybinds) setKeybinds( DEFAULT_KEYBINDS ) + + const key = data.code + const keyAlt = data.key.toLowerCase() // for legacy keybinds + + let command + for ( const [cmd, keybind] of Object.entries( keybinds as Object ) ) + if ( key === keybind || keyAlt === keybind ) + command = cmd + + if (!command) return + + switch (command) { + case "Seek Backward": + ytShorts.currentTime -= 5 + break + + case "Seek Forward": + ytShorts.currentTime += 5 + break + + case "Decrease Speed": + if (ytShorts.playbackRate > 0.25) ytShorts.playbackRate -= 0.25 + break + + case "Reset Speed": + ytShorts.playbackRate = 1 + break + + case "Increase Speed": + if (ytShorts.playbackRate < 16) ytShorts.playbackRate += 0.25 + break + + case "Increase Volume": + if (ytShorts.volume <= 0.975) { + setVolume(ytShorts.volume + 0.025) + } + break + + case "Decrease Volume": + if (ytShorts.volume >= 0.025) { + setVolume(ytShorts.volume - 0.025) + } + break + + case "Toggle Mute": + if ( !state.muted ) { + state.muted = true + state.volumeState = ytShorts.volume + ytShorts.volume = 0 + } else { + state.muted = false + ytShorts.volume = state.volumeState + } + break + + case "Next Frame": + if (ytShorts.paused) { + ytShorts.currentTime -= 0.04 + } + break + + case "Previous Frame": + if (ytShorts.paused) { + ytShorts.currentTime += 0.04 + } + break + + case "Next Short": + goToNextShort( ytShorts ) + break + + case "Previous Short": + goToPrevShort( ytShorts ) + break + } + setSpeed = ytShorts.playbackRate +}) + +// todo - fix this, move this +const setTimer = ( currTime: number, duration: number ) => { + const id = getCurrentId() + if ( document.getElementById(`ytTimer${id}`) === null ) return false + + const timerElement = document.getElementById( `ytTimer${id}` ) as HTMLElement + + timerElement.innerText = `${currTime}/${duration}s` + + return true +} + +// todo - generate this using my render() util method +const setVolumeSlider = ( ytShorts: HTMLVideoElement ) => { + const id = state.id + + const volumeContainer = getVolumeContainer(id) + const slider = document.createElement("input") + + if( state.actualVolume === null ) state.actualVolume = 0.5 + + // checkVolume(ytShorts) // todo - uncomment this when added + slider.id = `volumeSliderController${id}` + slider.classList.add("volume-slider") + slider.classList.add("betterYT-volume-slider") + slider.type = "range" + slider.min = "0" + slider.max = "1" + slider.step = "0.01" + slider.setAttribute("orient", "vertical") + volumeContainer.appendChild(slider) + slider.value = state.actualVolume + + slider.addEventListener( "input", e => setVolume( (e.target).valueAsNumber ) ) + + // Prevent video from pausing/playing on click + slider.addEventListener("click", (data) => { + data.stopPropagation() + }) +} + +// todo - move this to its own lib script (probably call it volumeSlider.ts) +const setVolume = ( volume: number ) => { + const id = getCurrentId() + const volumeSliderController = document.getElementById(`volumeSliderController${id}`) as HTMLInputElement + + if ( volumeSliderController === null ) return + + volumeSliderController.value = "" + volume + + // const ytShorts = document.querySelector( + // "#shorts-player > div.html5-video-container > video" + // ) as HTMLVideo + const ytShorts = getVideo() + + if ( ytShorts === null ) return + + const volumeData = { + data: { + volume: state.volume, + muted: state.muted, + } + } + + ytShorts.volume = volume + localStorage.setItem( "yt-player-volume", JSON.stringify( volumeData ) ) +} + +// todo - do this +// const checkVolume = ( ytShorts: HTMLVideoElement ) => { +// if(localStorage.getItem("yt-player-volume") !== null && JSON.parse(localStorage.getItem("yt-player-volume"))["data"]["volume"]){ +// actualVolume = JSON.parse(localStorage.getItem("yt-player-volume"))["data"]["volume"] +// ytShorts.volume = actualVolume +// }else{ +// actualVolume = ytShorts.volume +// } +// } + +const setPlaybackRate = ( currSpeed: number ) => { + const id = getCurrentId() + const playBackElement = document.getElementById( `ytPlayback${id}` ) as HTMLElement + + if ( playBackElement === null ) return false + + playBackElement.innerText = `${currSpeed}x` + + return true +} + +var injectedItem = new Set() +var lastTime = -1 +var lastSpeed = 0 +var setSpeed = 1 + +const timer = setInterval(() => { + if (window.location.toString().indexOf("youtube.com/shorts/") < 0) return + + const ytShorts = getVideo() + var currentId = getCurrentId() + var likeCount = getLikeCount( currentId ) + var actionList = getActionElement( currentId ) + var overlayList = getOverlayElement( currentId ) + var autoplayEnabled = localStorage.getItem("yt-autoplay") === "true" ? true : false + + if (autoplayEnabled === null) autoplayEnabled = false + + var progBarList = overlayList.children[2].children[0].children[0] + progBarList.removeAttribute( "hidden" ) + + if ( state.topId < state.currentId ) + state.topId = currentId + + // video has to have been playing to skip. + // I'm undecided whether to use 0.5 or 1 for currentTime, as 1 isn't quite fast enough, but sometimes with 0.5, it skips a video above the minimum like count. + if (ytShorts && ytShorts.currentTime > 0.5 && ytShorts.duration > 1) { + + if ( shouldSkipShort( state.currentId, likeCount ) ) { + console.log("[Better Youtube Shorts] :: Skipping short that had", likeCount, "likes") + state.skippedId = currentId + skipShort(ytShorts) + } + } + + if ( ytShorts === null ) return + + var currTime = -1 + if ( injectedItem.has( currentId ) ) { + + currTime = Math.round( ytShorts.currentTime ) + var currSpeed = ytShorts.playbackRate + + if (autoplayEnabled && ytShorts && ytShorts.currentTime >= ytShorts.duration - 0.11) { + var nextButton = getNextButton() + nextButton.click() + } + + if (currTime !== lastTime) { + // Using this as a check whether the elements actually were injected on the page + var injectedSuccess = setTimer(currTime, Math.round(ytShorts.duration || 0)) + // If failed, retry injection during next interval + if (!injectedSuccess) injectedItem.delete(currentId) + lastTime = currTime + } + if (currSpeed != lastSpeed) { + const setRateSuccess = setPlaybackRate(currSpeed) + if (setRateSuccess) lastSpeed = currSpeed + } + + } else { + lastTime = -1 + lastSpeed = 0 + if (autoplayEnabled && ytShorts) ytShorts.loop = false + + if (actionList) { + + const betterYTContainer = document.createElement("div") + betterYTContainer.id = "betterYT-container" + betterYTContainer.setAttribute("class", "button-container style-scope ytd-reel-player-overlay-renderer") + + const ytdButtonRenderer = document.createElement("div") + ytdButtonRenderer.setAttribute("class", "betterYT-renderer style-scope ytd-reel-player-overlay-renderer") + + const ytButtonShape = document.createElement("div") + ytButtonShape.setAttribute("class", "betterYT-button-shape") + + const ytLabel = document.createElement("label") + ytLabel.setAttribute("class", "yt-spec-button-shape-with-label") + + const ytButton = document.createElement("button") + ytButton.setAttribute("class", "yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-l yt-spec-button-shape-next--icon-button ") + // Playback Rate + var para0 = document.createElement("p") + para0.classList.add("betterYT") + para0.id = `ytPlayback${currentId}` + + // Timer + const ytTimer = document.createElement("div") + ytTimer.classList.add("yt-spec-button-shape-with-label__label") + var span1 = document.createElement("span") + span1.setAttribute("class", "yt-core-attributed-string yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--text-alignment-center yt-core-attributed-string--word-wrapping") + span1.id = `ytTimer${currentId}` + span1.setAttribute("role", "text") + ytTimer.appendChild(span1) + + // Match YT's HTML structure + ytButton.appendChild(para0) + ytLabel.appendChild(ytButton) + ytLabel.appendChild(ytTimer) + ytButtonShape.appendChild(ytLabel) + ytdButtonRenderer.appendChild(ytButtonShape) + betterYTContainer.appendChild(ytdButtonRenderer) + + actionList.insertBefore(betterYTContainer, actionList.children[1]) + + // Autoplay Switch + const switchContainer = document.createElement("div") + const autoplaySwitch = document.createElement("label") + autoplaySwitch.classList.add("autoplay-switch") + var checkBox = document.createElement("input") + checkBox.type = "checkbox" + checkBox.id = `autoplay-checkbox${currentId}` + checkBox.checked = autoplayEnabled + var autoplaySpan = document.createElement("span") + autoplaySpan.classList.add("autoplay-slider") + autoplaySwitch.append(checkBox, autoplaySpan) + switchContainer.appendChild(autoplaySwitch) + + actionList.insertBefore(switchContainer, actionList.children[1]) + + const autoplayTitle = document.createElement("div") + autoplayTitle.classList.add("yt-spec-button-shape-with-label__label") + var span2 = document.createElement("span") + span2.setAttribute("class", "betterYT-auto yt-core-attributed-string yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--text-alignment-center") + span2.setAttribute("role", "text") + span2.textContent = "Autoplay" + autoplayTitle.appendChild(span2) + + actionList.insertBefore(autoplayTitle, actionList.children[2]) + injectedItem.add(currentId) + + + ytShorts.playbackRate = setSpeed + setPlaybackRate(setSpeed) + injectedSuccess = setTimer( currTime || 0, Math.round(ytShorts.duration || 0)) + + betterYTContainer.addEventListener("click",() => { + ytShorts.playbackRate = 1 + setSpeed = ytShorts.playbackRate + }) + + checkBox.addEventListener('change', () => { + if (checkBox.checked) { + localStorage.setItem("yt-autoplay", "true") + ytShorts.loop = false + } else { + localStorage.setItem("yt-autoplay", "false") + ytShorts.loop = true + } + }) + + // todo - clean all of this up + wheel( + ytButton, + speedup, + speeddown + ) + + function speedup() + { + const video = getVideo() + if ( video === null ) return + + if (video.playbackRate < 16) video.playbackRate += 0.25 + setSpeed = video.playbackRate + } + function speeddown() + { + const video = getVideo() + if ( video === null ) return + + if (video.playbackRate > 0.25) video.playbackRate -= 0.25 + setSpeed = video.playbackRate + } + + wheel( ytTimer, forward, backward ) + function forward() + { + const video = getVideo() + if ( video !== null ) video.currentTime += 1 + } + + function backward() + { + const video = getVideo() + if ( video !== null ) video.currentTime -= 1 + } + + } + // Progress bar + // todo - move this to its own file + if ( overlayList ) + { + var progBarList = overlayList.children[2].children[0].children[0] + var progBarBG = progBarList.children[0] + var progBarPlayed = progBarList.children[1] // The red part of the progress bar + + const timestampTooltip = document.createElement("div") + timestampTooltip.classList.add("betterYT-timestamp-tooltip") + + progBarList.appendChild(timestampTooltip); + + // Styling to ensure rest of bottom overlay (shorts title/sub button) stay in place + (overlayList.children[0]).style.marginBottom = "-7px"; + (progBarList).style.height = "10px"; + (progBarList).style.paddingTop = "2px" ;// Slight padding to increase hover box + + progBarList.classList.add('betterYT-progress-bar') + progBarBG.classList.add('betterYT-progress-bar') + progBarPlayed.classList.add('betterYT-progress-bar') + + progBarList.addEventListener("mouseover", () => { + progBarBG.classList.add('betterYT-progress-bar-hover') + progBarPlayed.classList.add('betterYT-progress-bar-hover') + }) + progBarList.addEventListener("mousemove", (event) => { + let x = (event).clientX - ytShorts.getBoundingClientRect().left + // Deal with slight inaccuracies + if (x < 0) x = 0 + if (x > ytShorts.clientWidth) x = ytShorts.clientWidth + // Get timestamp and round to nearest 0.1 + let timestamp = ((x / ytShorts.clientWidth) * ytShorts.duration).toFixed(1) + timestampTooltip.textContent = `${timestamp}s` + // Ensure tooltip stays visible at edges of client + if ((x - (timestampTooltip.offsetWidth / 2)) > (ytShorts.clientWidth - timestampTooltip.offsetWidth)) { + timestampTooltip.style.left = `${ytShorts.clientWidth - timestampTooltip.offsetWidth}px` + } else if ((x - (timestampTooltip.offsetWidth / 2)) <= 0) { + timestampTooltip.style.left = "0px" + } else { + timestampTooltip.style.left = `${x - (timestampTooltip.offsetWidth / 2)}px` + } + timestampTooltip.style.top = "-20px" + timestampTooltip.style.display = 'block' + }) + progBarList.addEventListener("mouseout", () => { + progBarBG.classList.remove('betterYT-progress-bar-hover') + progBarPlayed.classList.remove('betterYT-progress-bar-hover') + timestampTooltip.style.display = 'none' + }) + progBarList.addEventListener("click", (event) => { + let x = (event).clientX - ytShorts.getBoundingClientRect().left + if (x < 0) x = 0 + if (x > ytShorts.clientWidth) x = ytShorts.clientWidth + ytShorts.currentTime = (x / ytShorts.clientWidth) * ytShorts.duration + }) + } + if (currentId !== null) setVolumeSlider( ytShorts ) + } + // if (ytShorts) checkVolume(ytShorts) // todo - uncomment this when added +}, 100) \ No newline at end of file diff --git a/src/css/popup.css b/src/css/popup.css new file mode 100644 index 0000000..4671bbe --- /dev/null +++ b/src/css/popup.css @@ -0,0 +1,364 @@ +@media (prefers-color-scheme: light) { + :root { + --text-primary-color: #030303; + --text-secondary-color: #888; + --bg-primary-color: #fff; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #f2f2f2; + --bg-secondary-hover-color: #e6e6e6; + --separation-line-color: #e5e5e5; + --yt-brand-color: #f00; + --suggested-action-color: #378de9; + --call-to-action-color: #3ea6ff; + } +} +@media (prefers-color-scheme: dark) { + :root { + --text-primary-color: #fff; + --text-secondary-color: #888; + --bg-primary-color: #0f0f0f; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #272727; + --bg-secondary-hover-color: #3d3d3d; + --separation-line-color: #3f3f3f; + --suggested-action-color: #1d5fd4; + --call-to-action-color: #3978e6; + } +} +:where(h1, +h2, +h3, +h4, +h5, +h6, +table, +tr, +td, +th, +p, +li, +span) { + color: var(--text-primary-color); +} + +body { + font-family: "Source Sans Pro", "Roboto", "Noto", "Arial", sans-serif; + background: var(--bg-primary-color); + color: var(--text-primary-color); + scrollbar-gutter: stable; +} + +.modal-content { + background: var(--bg-primary-color); + color: var(--text-primary-color); +} + +tr:not(:first-child) { + background-color: var(--bg-secondary-color); + transition: all 0.3s ease; +} + +tr:not(:first-child):hover { + background-color: var(--bg-secondary-hover-color); +} + +#keybind-input { + background-color: var(--bg-primary-hover-color); + caret-color: var(--text-primary-color); + color: var(--text-primary-color); +} + +.separation-line { + background-color: var(--separation-line-color); +} + +svg { + fill: var(--bg-primary-hover-color); + transition: all 0.5s ease; +} + +svg:hover { + cursor: pointer; + fill: var(--yt-brand-color) !important; +} + +.container { + width: 280px; +} + +.title-container { + display: flex; + justify-content: space-between; + flex-direction: row; +} + +.title { + font-size: 16px; + font-weight: bold; +} + +.version { + color: var(--bg-primary-hover-color); +} + +.separation-line { + height: 1px; + width: 100%; +} + +.textbox { + width: 100%; + font-size: 12px; + margin: 0; +} + +label { + font-size: 12px; +} + +#extra_options_skip_threshold { + margin-left: 4px; +} + +.textbox:focus { + outline: 0; + border-color: var(--call-to-action-color); +} + +.keybind-wrapper { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 10px; +} + +.keybind-span { + padding: 3px 7px; + background-color: var(--bg-secondary-hover-color); + border-radius: 3px; + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275); + cursor: default; +} + +.edit-svg { + fill: var(--bg-primary-hover-color); +} + +.edit-btn { + background: none; + outline: none; + border: none; +} + +table { + border-collapse: separate; + border-spacing: 0 2px; +} + +th { + font-size: 12px; + text-align: center; +} + +td { + font-size: 12px; + padding: 7px 5px; +} + +td:first-child { + padding-left: 6px; +} + +td:first-child, +th:first-child { + border-bottom-left-radius: 5px; + border-top-left-radius: 5px; + cursor: default; +} + +td:last-child, +th:last-child { + border-bottom-right-radius: 5px; + border-top-right-radius: 5px; +} + +.footer { + font-size: 10px; + text-align: center; + margin: 5px 0px; +} + +a { + color: var(--text-primary-color); + text-decoration: none; + display: flex; +} + +.btn-wrapper { + display: flex; + justify-content: center; + flex-direction: row; + gap: 8px; +} + +.btn { + margin: 5px 0px; + padding: 5px 10px; + background-color: var(--suggested-action-color); + border-radius: 3px; + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275); + cursor: pointer; + transition: all 0.3s ease; + font-size: 12px; +} + +.btn:hover { + background-color: var(--call-to-action-color); +} + +.modal { + display: none; + position: fixed; + z-index: 999; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.4); +} + +.modal-content { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + margin: auto; + padding: 0 8px 8px 8px; + width: 80%; + border-radius: 5px; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7); +} + +.modal-header { + display: flex; + justify-content: space-between; + flex-direction: row; + margin-bottom: 4px; + margin-top: 2px; + align-items: center; +} + +.modal-title { + font-weight: bold; +} + +.close-btn { + color: var(--bg-primary-hover-color); + font-size: 20px; + font-weight: bold; + text-decoration: none; + cursor: pointer; +} + +.close-btn:hover, +.close-btn:focus { + color: var(--yt-brand-color); + text-decoration: none; + cursor: pointer; +} + +.input-wrapper { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + margin: 10px 0px; + gap: 10px; +} + +.prevent-selection { + -webkit-user-select: none; /* Safari */ /* IE 10 and IE 11 */ + -moz-user-select: none; + user-select: none; /* Standard syntax */ +} + +#keybind-input { + width: 30%; + height: 1.5rem; + text-align: center; +} + +#keybind-input:focus { + outline: 0; + outline: none !important; + box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961); +} + +.extra_options--row { + display: grid; + grid-template-columns: 5fr 1fr; + text-wrap: balance; + margin-bottom: 16px; +} + +.extra_options--row:last-child() { + margin-bottom: 0; +} + +.extra_options--row input[type=number], +.extra_options--row input[type=text] { + outline: none; + border: 1px var(--bg-secondary-hover-color) solid; + background: var(--bg-secondary-hover-color); + color: var(--text-primary-color); + padding: 0 1rem; + border-radius: 100vh; + width: 5rem; +} + +.extra_options--row input[type=number]:focus, +.extra_options--row input[type=text]:focus { + border: 2px var(--yt-brand-color) solid; +} + +.extra_options--row input[type=checkbox], +.extra_options--row input[type=radio], +.extra_options--row input[type=range] { + accent-color: var(--yt-brand-color); +} + +.extra_options--row input[type=number]::-webkit-outer-spin-button, +.extra_options--row input[type=number]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* Firefox */ +.extra_options--row input[type=number] { + -moz-appearance: textfield; +} + +/* width */ +::-webkit-scrollbar { + width: 0.5rem; +} + +/* Track */ +::-webkit-scrollbar-track { + background: transparent; + height: 4rem; +} + +/* Handle */ +::-webkit-scrollbar-thumb { + background: var(--bg-secondary-color); + height: 4rem; + border-radius: 1rem; +} + +/* Handle on hover */ +::-webkit-scrollbar-thumb:hover { + background: var(--bg-secondary-hover-color); +}/*# sourceMappingURL=popup.css.map */ \ No newline at end of file diff --git a/src/css/popup.css.map b/src/css/popup.css.map new file mode 100644 index 0000000..728e609 --- /dev/null +++ b/src/css/popup.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["popup.scss","popup.css"],"names":[],"mappings":"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","file":"popup.css","sourcesContent":[":root {\n\t\n}\n\n@media (prefers-color-scheme: light) \n{\n :root\n {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n }\n}\n@media (prefers-color-scheme: dark) \n{\n :root\n {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\nbody {\n\tfont-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n\tbackground: var(--bg-primary-color);\n\tcolor: var(--text-primary-color);\n scrollbar-gutter: stable;\n}\n.modal-content {\n\tbackground: var(--bg-primary-color);\n\tcolor: var(--text-primary-color);\n}\ntr:not(:first-child) {\n\tbackground-color: var(--bg-secondary-color);\n\ttransition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n\tbackground-color: var(--bg-secondary-hover-color);\n}\n#keybind-input {\n\tbackground-color: var(--bg-primary-hover-color);\n\tcaret-color: var(--text-primary-color);\n\tcolor: var(--text-primary-color);\n}\n.separation-line {\n\tbackground-color: var(--separation-line-color);\n}\n\nsvg {\n\tfill: var(--bg-primary-hover-color);\n\ttransition: all 0.5s ease;\n}\n\nsvg:hover {\n\tcursor: pointer;\n\tfill: var(--yt-brand-color) !important;\n}\n\n.container {\n\twidth: 280px;\n}\n\n.title-container {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tflex-direction: row;\n}\n\n.title {\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.version {\n\tcolor: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n\theight: 1px;\n\twidth: 100%;\n}\n\n.textbox {\n\twidth: 100%;\n\tfont-size: 12px;\n\tmargin: 0;\n}\n\nlabel {\n\tfont-size: 12px;\n}\n\n#extra_options_skip_threshold {\n\tmargin-left: 4px;\n}\n\n.textbox:focus {\n\toutline: 0;\n\tborder-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n\tdisplay: flex;\n\tflex-direction: row;\n\tjustify-content: center;\n\talign-items: center;\n\tgap: 10px;\n}\n\n.keybind-span {\n\tpadding: 3px 7px;\n\tbackground-color: var(--bg-secondary-hover-color);\n\tborder-radius: 3px;\n\tbox-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n\tcursor: default;\n}\n\n.edit-svg {\n\tfill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n\tborder-collapse: separate;\n\tborder-spacing: 0 2px;\n}\n\nth {\n\tfont-size: 12px;\n\ttext-align: center;\n}\n\ntd {\n\tfont-size: 12px;\n\tpadding: 7px 5px;\n}\n\ntd:first-child {\n\tpadding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n\tborder-bottom-left-radius: 5px;\n\tborder-top-left-radius: 5px;\n\tcursor: default;\n}\n\ntd:last-child,\nth:last-child {\n\tborder-bottom-right-radius: 5px;\n\tborder-top-right-radius: 5px;\n}\n\n.footer {\n\tfont-size: 10px;\n\ttext-align: center;\n\tmargin: 5px 0px;\n}\n\na {\n\tcolor: var(--text-primary-color);\n\ttext-decoration: none;\n\tdisplay: flex;\n}\n\n.btn-wrapper {\n\tdisplay: flex;\n\tjustify-content: center;\n\tflex-direction: row;\n\tgap: 8px;\n}\n\n.btn {\n\tmargin: 5px 0px;\n\tpadding: 5px 10px;\n\tbackground-color: var(--suggested-action-color);\n\tborder-radius: 3px;\n\tbox-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n\tcursor: pointer;\n\ttransition: all 0.3s ease;\n\tfont-size: 12px;\n}\n\n.btn:hover {\n\tbackground-color: var(--call-to-action-color);\n}\n\n.modal {\n\tdisplay: none;\n\tposition: fixed;\n\tz-index: 999;\n\tleft: 0;\n\ttop: 0;\n\twidth: 100%;\n\theight: 100%;\n\toverflow: auto;\n\tbackground-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n\tposition: absolute;\n\tleft: 50%;\n\ttop: 50%;\n\ttransform: translate(-50%, -50%);\n\tmargin: auto;\n\tpadding: 0 8px 8px 8px;\n\twidth: 80%;\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.modal-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tflex-direction: row;\n\tmargin-bottom: 4px;\n\tmargin-top: 2px;\n\talign-items: center;\n}\n\n.modal-title {\n\tfont-weight: bold;\n}\n\n.close-btn {\n\tcolor: var(--bg-primary-hover-color);\n\tfont-size: 20px;\n\tfont-weight: bold;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n\tcolor: var(--yt-brand-color);\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.input-wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: space-between;\n\talign-items: center;\n\tmargin: 10px 0px;\n\tgap: 10px;\n}\n\n.prevent-selection {\n\t-webkit-user-select: none; /* Safari */\n\t-ms-user-select: none; /* IE 10 and IE 11 */\n\tuser-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n\twidth: 30%;\n\theight: 1.5rem;\n\ttext-align: center;\n}\n\n#keybind-input:focus {\n\toutline: 0;\n\toutline: none !important;\n\tbox-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}","@media (prefers-color-scheme: light) {\n :root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n }\n}\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n scrollbar-gutter: stable;\n}\n\n.modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n#keybind-input {\n background-color: var(--bg-primary-hover-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.btn {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: var(--suggested-action-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n\n.btn:hover {\n background-color: var(--call-to-action-color);\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n margin-bottom: 4px;\n margin-top: 2px;\n align-items: center;\n}\n\n.modal-title {\n font-weight: bold;\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}"]} \ No newline at end of file diff --git a/popup.css b/src/css/popup.scss similarity index 87% rename from popup.css rename to src/css/popup.scss index abaf9ed..79da182 100644 --- a/popup.css +++ b/src/css/popup.scss @@ -1,26 +1,37 @@ :root { - --text-primary-color: #030303; - --text-secondary-color: #888; - --bg-primary-color: #fff; - --bg-primary-hover-color: #9d9ea1; - --bg-secondary-color: #f2f2f2; - --bg-secondary-hover-color: #e6e6e6; - --separation-line-color: #e5e5e5; - --yt-brand-color: #f00; - --suggested-action-color: #378de9; - --call-to-action-color: #3ea6ff; -} - -[data-theme='dark'] { - --text-primary-color: #fff; - --text-secondary-color: #888; - --bg-primary-color: #0f0f0f; - --bg-primary-hover-color: #9d9ea1; - --bg-secondary-color: #272727; - --bg-secondary-hover-color: #3d3d3d; - --separation-line-color: #3f3f3f; - --suggested-action-color: #1d5fd4; - --call-to-action-color: #3978e6; + +} + +@media (prefers-color-scheme: light) +{ + :root + { + --text-primary-color: #030303; + --text-secondary-color: #888; + --bg-primary-color: #fff; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #f2f2f2; + --bg-secondary-hover-color: #e6e6e6; + --separation-line-color: #e5e5e5; + --yt-brand-color: #f00; + --suggested-action-color: #378de9; + --call-to-action-color: #3ea6ff; + } +} +@media (prefers-color-scheme: dark) +{ + :root + { + --text-primary-color: #fff; + --text-secondary-color: #888; + --bg-primary-color: #0f0f0f; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #272727; + --bg-secondary-hover-color: #3d3d3d; + --separation-line-color: #3f3f3f; + --suggested-action-color: #1d5fd4; + --call-to-action-color: #3978e6; + } } :where( @@ -36,7 +47,7 @@ th, p, li, - span, + span ){ color: var(--text-primary-color); } diff --git a/src/lib/changeShort.ts b/src/lib/changeShort.ts new file mode 100644 index 0000000..5cc6a89 --- /dev/null +++ b/src/lib/changeShort.ts @@ -0,0 +1,8 @@ +export function goToNextShort( ytShorts: HTMLVideoElement ) +{ + console.warn("[BYS] :: Unimplemented"); +} +export function goToPrevShort( ytShorts: HTMLVideoElement ) +{ + console.warn("[BYS] :: Unimplemented"); +} \ No newline at end of file diff --git a/src/lib/declarations.ts b/src/lib/declarations.ts new file mode 100644 index 0000000..5f79ee8 --- /dev/null +++ b/src/lib/declarations.ts @@ -0,0 +1,128 @@ +import BROWSER from "../background/browser"; +import { NumberDictionary, PolyDictionary, StateObject, StringDictionary } from "./types"; + +export const VERSION = BROWSER.runtime.getManifest().version + +export const DEFAULT_KEYBINDS = { + "Seek Backward": "ArrowLeft", + "Seek Forward": "ArrowRight", + "Decrease Speed": "KeyU", + "Reset Speed": "KeyI", + "Increase Speed": "KeyO", + "Decrease Volume": "Minus", + "Increase Volume": "Equal", + "Toggle Mute": "KeyM", + "Next Frame": "Comma", + "Previous Frame": "Period", + "Next Short": "KeyS", + "Previous Short": "KeyW", +}; + +export const DEFAULT_OPTIONS = { + skip_enabled: false, + skip_threshold: 500, +} + +export var keybinds: StringDictionary = null +export const setKeybinds = ( newKeybinds: StringDictionary ) => keybinds = newKeybinds + +export var options: PolyDictionary = null +export const setOptions = ( newOptions: PolyDictionary ) => keybinds = newOptions + +export const storage = BROWSER.storage.local + +const stateObject = { + id : 0, + topId : 0, + volumeState : 0, + + actualVolume: null, + skippedId : null, + + muted : false, +} +export const state = new Proxy( stateObject, { + set: ( o: StateObject, prop: string, val: any ) => { + o[ prop ] = val + return true + } +} ) + +// todo - add formats from other langs (note: dont include duplicate keys)# +export const NUMBER_MODIFIERS: NumberDictionary = { + // English + "b": 1_000_000_000, + "m": 1_000_000, + "k": 1_000, + + // Italian + "mln": 1_000_000, + + // Indian English + "lakh": 100_000, + + // Portuguese + "mil": 1_000, + + // French + "mio": 1_000_000, + "md": 1_000, + + // German + "mrd": 1_000_000_000, + "tsd": 1_000, + + // Japanese + "億": 1_000_000_000, + "万": 10_000, + + // Chinese (Simplified) + "亿": 1_000_000_000, + + // Chinese (Traditional) + "萬": 10_000, + + // Russian + "млн": 1_000_000, + "тыс": 1_000, + + // Hindi + "करोड़": 10_000_000, + "लाख": 100_000, + + // Arabic + "مليون": 1_000_000, + "مليار": 1_000_000_000, + "ألف": 1_000, + + // Korean + "억": 100_000_000, + "만": 10_000, + + // Turkish + "milyon": 1_000_000, + "milyar": 1_000_000_000, + "bin": 1_000, + + // Vietnamese + "triệu": 1_000_000, + "tỷ": 1_000_000_000, + "nghìn": 1_000, + + // Thai + "ล้าน": 1_000_000, + "พันล้าน": 1_000_000_000, + "พัน": 1_000, + + // Dutch + "mld": 1_000_000_000, + + // Greek + "εκ": 1_000_000, + "δισ": 1_000_000_000, + "χιλ": 1_000, + + // Swedish + "mn": 1_000_000, + "t": 1_000, +} \ No newline at end of file diff --git a/src/lib/getters.ts b/src/lib/getters.ts new file mode 100644 index 0000000..72f202a --- /dev/null +++ b/src/lib/getters.ts @@ -0,0 +1,71 @@ +// todo - cleanup, convert to named functions and add return types. + +import { convertLocaleNumber } from "./utils" + +export function getCurrentId() +{ + const video = document.querySelector( "#shorts-player > div.html5-video-container > video" ) as HTMLVideoElement + if ( video === null ) return null + + const closest = video.closest("ytd-reel-video-renderer" ) as HTMLElement + if ( closest === null ) return null + + return +closest.id +} + +export function getLikeCount( id: number | null ): number +{ + const likesElement = document.querySelector( + `[id="${id}"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer #like-button` + ) as HTMLElement + + // Use optional chaining and nullish coalescing to handle null values + const numberOfLikes = (likesElement?.firstElementChild)?.innerText.split(/\r?\n/)[0]?.trim().replace(/\s/g, "").replace(/\.$/, "").toLowerCase() ?? "0" + + // Convert the number of likes to the appropriate format + const likeCount = convertLocaleNumber( numberOfLikes ) as number + + // If likeCount is anything other than a number, it"ll return 0. Meaning it"ll translate every language. + return !isNaN(likeCount) ? likeCount as number : 0 +} + +// Checking comment count aswell, as sometimes popular videos bug out and show 0 likes, but there"s 1000+ comments. +export const getCommentCount = ( id: number | null ) => { + const commentsElement = document.querySelector( + `[id="${id}"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer #comments-button` + ) as HTMLElement + + // Use optional chaining and nullish coalescing to handle null values + const numberOfComments = (commentsElement?.firstElementChild)?.innerText.split(/\r?\n/)[0]?.replace(/ /g, "") ?? "0" + + // Convert the number of comments to the appropriate format + const commentCount = convertLocaleNumber(numberOfComments) + + // If commentCount is anything other than a number, it"ll return 0. Meaning it"ll handle every language. + return !isNaN(commentCount as number) ? commentCount : 0 +} + +export const getActionElement = ( id: number | null ) => + document.querySelector( + `[id="${id}"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #actions` + ) as HTMLElement + +export const getOverlayElement = ( id: number | null ) => + document.querySelector( + `[id="${id}"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #overlay` + ) as HTMLElement + +export const getVolumeContainer = ( id: number | null ) => + document.querySelector( + `[id="${id}"] > #player-container > div.player-controls.style-scope.ytd-reel-video-renderer > ytd-shorts-player-controls.style-scope.ytd-reel-video-renderer` + ) as HTMLElement + +export const getNextButton = () => + document.querySelector( + "#navigation-button-down > .style-scope.ytd-shorts > yt-button-shape > button.yt-spec-button-shape-next.yt-spec-button-shape-next--text.yt-spec-button-shape-next--mono.yt-spec-button-shape-next--size-xl.yt-spec-button-shape-next--icon-button" + ) as HTMLElement + +export function getVideo(): HTMLVideoElement | null +{ + return document.querySelector( "#shorts-player>div>video" ) +} diff --git a/src/lib/skipShort.ts b/src/lib/skipShort.ts new file mode 100644 index 0000000..18d533b --- /dev/null +++ b/src/lib/skipShort.ts @@ -0,0 +1,46 @@ +// video with no likes => https://www.youtube.com/shorts/ZFLRydDd9Mw +// video with no likes and 23k comments => https://www.youtube.com/shorts/gISsypl5xsc +// another => https://www.youtube.com/shorts/qe56pgRVrgE?feature=share +// video with 1.5M / 1,5M => https://www.youtube.com/shorts/nKZIx1bHUbQ + +import { options, state } from "./declarations" +import { getNextButton, getVideo } from "./getters" + +export function shouldSkipShort( currentId: string, likeCount: number ) +{ + // for debugging purposes + + // console.dir({ + // "extra options check": !( extraOptions == null ), + // "video playing check": !( getVideo().currentTime === 0 ), + // "option enabled?": !( !extraOptions.skip_enabled ), + // "current id check": !( currentId < topId ), + // "skipped id check": !( skippedId === currentId ), + // "likecount null check": !( likeCount === null || isNaN( likeCount ) ), + // "threshold check": !( likeCount >= extraOptions.skip_threshold ), + // "current threshold": extraOptions.skip_threshold, + // "number of likes": likeCount + // }) + + if ( options === null ) return false + if ( getVideo()?.currentTime === 0 ) return false // video unstarted, likes likely not loaded + + if ( !options.skip_enabled ) return false + if ( currentId < state.topId ) return false // allow user to scroll back up to see skipped video + if ( state.skippedId === currentId ) return false // prevent skip spam + if ( likeCount === null || isNaN( likeCount ) ) return false // dont skip unloaded shorts + if ( likeCount >= options.skip_threshold ) return false + + return true +} + +/** + * If the setting `shouldSkipUnrecommendedShorts` is true, skip shorts that have fewer than the set number of likes + */ + + // fixed mac scroll issue +export function skipShort( short: HTMLVideoElement ) +{ + var nextButton = getNextButton(); + nextButton.click(); +} diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000..c7afcd5 --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,15 @@ +export type NumberDictionary = { + [key: string]: number +} | null + +export type StringDictionary = { + [key: string]: string +} | null + +export type PolyDictionary = { + [key: string]: any +} | null + +export interface StateObject { + [key: string]: any +} \ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..a1169c1 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,70 @@ +import { NUMBER_MODIFIERS } from "./declarations" + +/** + * Converts a formatted number to its full integer value. + * @param {string} string value to be converted (eg: 1.4M, 1,291 or 727) + * @returns converted number + */ +export function convertLocaleNumber( string: string ): number | null +{ + if ( typeof string !== "string" ) return null + + const regex = /^(\d{1,3}(?:(?:,\d{3})*(?:\.\d+)?)|(?:\d+))(?:([,.])(\d+))?([a-z]*)\.?$/i + const matches = string.match(regex) + + if (!matches) { + return 0 + } + + let numericPart = matches[1].replace(/,/g, "") // Remove commas + if (matches[2] && matches[3]) { + // Decimal part exists, add it back + numericPart += `.${matches[3]}` + } + + const multiplier = matches[4].toLowerCase() + const modifier = ( NUMBER_MODIFIERS === null ) ? null : NUMBER_MODIFIERS[multiplier] + + if ( modifier !== null ) + { + return +numericPart * modifier + } + else + { + // Remove decimals and commas from the numeric part + const numericValue = parseInt(numericPart.replace(/[.,]/g, ""), 10) + return numericValue + } +} + +// todo - fix types on this +export function wheel( element: HTMLElement, codeA: () => void, codeB: () => void ) { + element.addEventListener( "wheel", ( e: WheelEvent ) => { + e.preventDefault() + + if (e.deltaY > 0) codeA() + else codeB() + + }, { passive: false } + ) +} + +/** + * Take an HTML string and convert it to a live HTML element. + * + * This returns the element(s) themselves, and can be manipulated as such + * + * The string must have a single parent (no siblings on the highest level, eg how react does it) + * + * @param htmlString eg: "\

\" + * @returns HTML Element (or null if the string parses to no HTML elements) + */ +export function render( htmlString: string ): Node +{ + const elements = new DOMParser().parseFromString( htmlString, "text/html" ).body.children + + if ( elements.length > 1 ) throw new Error( "ADSU | HTML String cannot have siblings!" ) + if ( elements.length < 1 ) throw new Error( "ADSU | HTML String must have an element!" ) + + return elements[0] as Node +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..063f282 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,17 @@ +/** + * main.tsx + * + * This is where the react code is injected. + * For content-script code (that which is injected onto the page), + * see ./content.ts + */ + +import React from 'react' +import ReactDOM from 'react-dom/client' +import Popup from './components/Popup' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +) diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/styles.css b/styles.css deleted file mode 100644 index 516c08f..0000000 --- a/styles.css +++ /dev/null @@ -1,125 +0,0 @@ -.betterYT { - font-size: 16px; - font-weight: bold; - text-align: center; -} - -.betterYT-renderer { - display: inline-block; -} - -.betterYT-button-shape { - display: flex; -} - -.betterYT-volume-slider{ - -webkit-appearance: slider-vertical; - position: absolute; - right: -40px; - top: 40px; - opacity: 0.4; - pointer-events: all !important; - cursor: pointer; -} - -@supports (-moz-appearance:none) { - .volume-slider{ - right: 16px; - } -} - -.volume-slider:hover{ - opacity: 1; -} - -.autoplay-switch { - position: relative; - display: inline-block; - width: 48px; - height: 27px; -} - -/* Hide default HTML checkbox */ -.autoplay-switch input { - opacity: 0; - width: 0; - height: 0; -} - -.autoplay-slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #272727; - -webkit-transition: .4s; - transition: .4s; - border-radius: 27px; -} - -.autoplay-slider:hover { - background-color: #3f3f3f; -} - -.autoplay-slider:before { - position: absolute; - content: ""; - height: 20px; - width: 20px; - left: 3px; - bottom: 3px; - background-color: white; - -webkit-transition: .4s; - transition: .4s; - border-radius: 50%; -} - -input:checked + .autoplay-slider { - background-color: #3f3f3f; -} - -input:checked + .autoplay-slider:before { - -webkit-transform: translateX(21px); - -ms-transform: translateX(21px); - transform: translateX(21px); -} - -@media screen and (max-width: 599px) { - .volumeSlider { - -webkit-appearance: slider-horizontal; - right: 44px; - top: 18px; - } - .autoplay-slider { - background-color: #FFFFFF1A; - } - input:checked + .autoplay-slider { - background-color: #FFFFFF2A; - } - .betterYT-auto { - padding-bottom: 16px; - } -} - -.betterYT-progress-bar{ - bottom: 0; - pointer-events: auto !important; -} - -.betterYT-progress-bar-hover{ - height: 10px !important; - cursor: pointer; -} - -.betterYT-timestamp-tooltip{ - position: absolute; - display: none; - background-color: #272727c4; - border-radius: 5px; - padding: 4px; - font-size: 11px; - color: white; - z-index: 50; -} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3d0a51a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..e993792 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "composite": true, + "module": "esnext", + "moduleResolution": "node" + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..29edff3 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import { crx } from '@crxjs/vite-plugin' +import manifest from './manifest.json' + +export default defineConfig({ + plugins: [ + react(), + crx({ manifest }), + ], +}) \ No newline at end of file From 624e215a03e49e0d95ab1c58df6abece3ee38714 Mon Sep 17 00:00:00 2001 From: adsuth Date: Fri, 4 Aug 2023 04:34:53 +0100 Subject: [PATCH 02/60] WIP on the popup --- src/components/EditButton.tsx | 47 +++++++ src/components/Header.tsx | 15 +++ src/components/KeybindsTable.tsx | 71 ++++++++++ src/components/OptionsTable.tsx | 112 ++++++++++++++++ src/components/PageIndicator.tsx | 24 ++++ src/components/PageIndicatorContainer.tsx | 30 +++++ src/components/Popup.tsx | 95 ++++++++------ src/components/Separator.tsx | 7 + src/content.ts | 43 ++---- src/css/popup.css | 128 ++++++++++++++---- src/css/popup.css.map | 2 +- src/css/popup.scss | 152 +++++++++++++++++----- src/lib/ResetDefaults.ts | 25 ++++ src/lib/declarations.ts | 51 +++++++- src/lib/{types.ts => definitions.ts} | 13 ++ src/lib/retrieveFromStorage.ts | 51 ++++++++ src/lib/utils.ts | 50 ++++++- 17 files changed, 767 insertions(+), 149 deletions(-) create mode 100644 src/components/EditButton.tsx create mode 100644 src/components/Header.tsx create mode 100644 src/components/KeybindsTable.tsx create mode 100644 src/components/OptionsTable.tsx create mode 100644 src/components/PageIndicator.tsx create mode 100644 src/components/PageIndicatorContainer.tsx create mode 100644 src/components/Separator.tsx create mode 100644 src/lib/ResetDefaults.ts rename src/lib/{types.ts => definitions.ts} (56%) create mode 100644 src/lib/retrieveFromStorage.ts diff --git a/src/components/EditButton.tsx b/src/components/EditButton.tsx new file mode 100644 index 0000000..1a81483 --- /dev/null +++ b/src/components/EditButton.tsx @@ -0,0 +1,47 @@ +import React from 'react' +import { setKeybind, storage } from '../lib/declarations' +import { StringDictionary } from '../lib/definitions' + +const edit_button_svg = (<> + + + + + + + + + +) + +interface Props { + keybindsState: StringDictionary, + setKeybindsState: any, // ! give specific type + command: string, +} +export default function EditButton( { keybindsState, setKeybindsState, command }: Props ) { + + // todo - add on click event + props + + function handleEditButtonClick( command: string ): void + { + console.log( `Clicked: ${command}` ) + + // setKeybindState( () => { + // const newState = setKeybind( keybindState, command, key ) + + // storage.set( { "extraopts" : newState } ) + // localStorage.setItem( "yt-extraopts", JSON.stringify( newState ) ) + + // console.log( `[BYS] :: Set keybind "${command}" to ${key}` ) + + // return newState + // } ) + } + + return ( + + ) +} diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..55a5a29 --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import { VERSION } from '../lib/declarations' +import bys_logo from "../assets/icons/bys-48.png" + +export default function Header() { + return ( +

+
+
Better Youtube Shorts
+
v{VERSION}
+
+ Better Youtube Shorts logo +
+) +} diff --git a/src/components/KeybindsTable.tsx b/src/components/KeybindsTable.tsx new file mode 100644 index 0000000..28531eb --- /dev/null +++ b/src/components/KeybindsTable.tsx @@ -0,0 +1,71 @@ +import React from 'react' +import { keybinds } from '../lib/declarations' +import EditButton from './EditButton' +import { resetKeybinds } from '../lib/ResetDefaults' +import { StringDictionary } from '../lib/definitions' + +interface Props +{ + setKeybindsState: ( keybinds: () => StringDictionary ) => void + keybindsState: StringDictionary +} + +export default function KeybindsTable( { setKeybindsState, keybindsState }: Props ) { + + function handleResetKeybinds() + { + setKeybindsState( () => { + resetKeybinds() + return keybinds + } ) + } + + function populateKeybindsTable() + { + return Object.entries( keybindsState as Object ).map( ( [ command, bind ] ) => { + + const editButtonProps = { + keybindsState, setKeybindsState, command + } + + return ( + + {command} + +
+ {bind} +
+ + + + + + ) + } ) + } + + return ( + <> +

Keybinds

+ + + + + + + + + + { populateKeybindsTable() } + +
CommandKey
+ + + + ) +} diff --git a/src/components/OptionsTable.tsx b/src/components/OptionsTable.tsx new file mode 100644 index 0000000..3156234 --- /dev/null +++ b/src/components/OptionsTable.tsx @@ -0,0 +1,112 @@ +import React, { useState } from 'react' +import { DEFAULT_OPTIONS, OPTION_DICTIONARY, options, setOption, setOptions, storage } from '../lib/declarations' +import { determineInputType } from '../lib/utils' +import { PolyDictionary } from '../lib/definitions' +import { resetOptions } from '../lib/ResetDefaults' + +interface Props +{ + optionsState: PolyDictionary + setOptionsState: ( options: ( previousState: PolyDictionary ) => PolyDictionary ) => void +} + +const TYPES_THAT_USE_ON_CHANGE = [ "checkbox", "range" ] + +export default function OptionsTable( { optionsState, setOptionsState }: Props ) { + // this only exists to rerender on change + + function handleResetOptionsClick() + { + setOptionsState( () => { + resetOptions() + return options + } ) + } + + function handleOptionChange( e: any, option: string ) + { + if ( optionsState === null ) return + + const target = e.target as HTMLInputElement + let value: any = target.value + + // this may need changed depending on different input types + if ( target.type === "checkbox" ) value = target.checked + else if ( !isNaN( target.valueAsNumber ) ) value = target.valueAsNumber + + if ( value === null ) return console.warn( `[BYS] :: Option set handler tried to set option ${option} to null` ) + + // if value is number, handle min and max ranges + if ( [ "number", "range" ].includes( target.type ) ) + { + if ( target?.max !== "" ) + if ( +value > +target.max ) + value = +target.max + + if ( target?.min !== "" ) + if ( +value < +target.min ) + value = +target.min + } + + // set in storage + setOptionsState( () => { + const newState = setOption( optionsState, option, value ) + + storage.set( { "extraopts" : newState } ) + localStorage.setItem( "yt-extraopts", JSON.stringify( newState ) ) + + console.log( `[BYS] :: Set Option "${option}" to ${value}` ) + + return newState + } ) + + } + + function populateOptionsTable() + { + return Object.entries( optionsState as Object ).map( ( [option, value] ) => { + if ( OPTION_DICTIONARY === null ) return <> + + const type = determineInputType( value ) + + if ( optionsState === null ) return + + return ( +
+ + handleOptionChange( e, option ) : undefined } + onInput = { ( !TYPES_THAT_USE_ON_CHANGE.includes( type ) ) ? e => handleOptionChange( e, option ) : undefined } + /> +
+ ) + } ) + } + + return ( + <> +

Extra Options

+ +
+ { populateOptionsTable() } +
+ + + + ) +} diff --git a/src/components/PageIndicator.tsx b/src/components/PageIndicator.tsx new file mode 100644 index 0000000..6951a50 --- /dev/null +++ b/src/components/PageIndicator.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { PopupPageNameEnum } from '../lib/definitions' +import { capitalise, getEnumWithString } from '../lib/utils' + +interface Props +{ + page: string + setCurrentPage: ( page: PopupPageNameEnum ) => void + isCurrentPage: boolean +} + +export default function PageIndicator( {page, setCurrentPage, isCurrentPage}: Props ) { + + function handlePageIndicatorClick( page: string ) + { + setCurrentPage( getEnumWithString( PopupPageNameEnum, page, 0 ) ) + } + + return ( + + ) +} diff --git a/src/components/PageIndicatorContainer.tsx b/src/components/PageIndicatorContainer.tsx new file mode 100644 index 0000000..81c7ac0 --- /dev/null +++ b/src/components/PageIndicatorContainer.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import PageIndicator from './PageIndicator' +import { PopupPageNameEnum } from '../lib/definitions' +import { getEnumEntries } from '../lib/utils' + +interface Props +{ + currentPage: PopupPageNameEnum + setCurrentPage: ( newPage: PopupPageNameEnum ) => void +} + +export default function PageIndicatorContainer( { currentPage, setCurrentPage }: Props ) { + + function populatePageIndicators() + { + return getEnumEntries( PopupPageNameEnum ).map( ([name, page]) => { + const isCurrentPage = currentPage === page + + const props = { page: name, setCurrentPage, isCurrentPage } + + return + } ) + } + + return ( +
+ { populatePageIndicators() } +
+ ) +} diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx index 672d6e0..9361e42 100644 --- a/src/components/Popup.tsx +++ b/src/components/Popup.tsx @@ -1,57 +1,66 @@ +import { useEffect, useState } from "react" import "../css/popup.css" -import bys_logo from "../assets/icons/bys-48.png" -import { VERSION } from "../lib/declarations" +import Header from "./Header" +import KeybindsTable from "./KeybindsTable" +import OptionsTable from "./OptionsTable" +import Separator from "./Separator" +import PageIndicatorContainer from "./PageIndicatorContainer" +import { PolyDictionary, PopupPageNameEnum, StringDictionary } from "../lib/definitions" +import { keybinds, options } from "../lib/declarations" +import { retrieveKeybindsFromStorage, retrieveOptionsFromStorage } from "../lib/retrieveFromStorage" // todo - split this into its component parts function Popup() { + useEffect( () => { + retrieveOptionsFromStorage( setOptionsState ) + retrieveKeybindsFromStorage( setKeybindsState ) + }, [] ) + + const [ currentPage, setCurrentPage ] = useState( PopupPageNameEnum.KEYBINDS ) + const currentPageProps = { currentPage, setCurrentPage } + + const [ keybindsState, setKeybindsState ] = useState( keybinds ) + const [ optionsState, setOptionsState ] = useState( options ) + + + const keybindsProp = { keybindsState, setKeybindsState } + const optionsProp = { optionsState, setOptionsState } + + function getCurrentPageContent() + { + if ( currentPage === PopupPageNameEnum.KEYBINDS ) return + if ( currentPage === PopupPageNameEnum.OPTIONS ) return + } + return ( - <> -
-
-
-
-
Better Youtube Shorts
-
v{VERSION}
-
- logo -
-
-

Keybinds

- -
-

Extra Options

-
-
- - -
-
- - -
+
+
+ + + + + + + + { getCurrentPageContent() } + +
+
+
+
Edit keybind:
+ ×
-
Reload the page for changes to take effect.
-
Reset keybindsGitHub
- {/* Modal */} -
-
-
-
Edit keybind:
- × -
-
-
- - -
Does not support key combinations
-
-
+
+
+ + +
Does not support key combinations
- +
) } diff --git a/src/components/Separator.tsx b/src/components/Separator.tsx new file mode 100644 index 0000000..d6ca33e --- /dev/null +++ b/src/components/Separator.tsx @@ -0,0 +1,7 @@ +import React from 'react' + +export default function Separator() { + return ( +
+ ) +} diff --git a/src/content.ts b/src/content.ts index 3204a12..6d01c04 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,10 +1,13 @@ import { handleColorScheme } from "./background/handleColorScheme" import { goToNextShort, goToPrevShort } from "./lib/changeShort" -import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, keybinds, setKeybinds, state, storage } from "./lib/declarations" +import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, keybinds, setKeybinds, setOptions, state, storage } from "./lib/declarations" import { getActionElement, getCurrentId, getLikeCount, getNextButton, getOverlayElement, getVideo, getVolumeContainer } from "./lib/getters" +import { retrieveKeybindsFromStorage, retrieveOptionsFromStorage } from "./lib/retrieveFromStorage" import { shouldSkipShort, skipShort } from "./lib/skipShort" import { wheel } from "./lib/utils" + + /** * content.ts * @@ -12,43 +15,13 @@ import { wheel } from "./lib/utils" * For popup code, see ./main.tsx */ +// todo - also retrieve our keybinds (move that to its own file) +retrieveOptionsFromStorage( setOptions ) +retrieveKeybindsFromStorage( setOptions ) + // watch for color scheme changes window.matchMedia( "(prefers-color-scheme: dark)" ).addEventListener( "change", ({matches}) => handleColorScheme( matches ) ) - -// Using localStorage as a fallback for browser/chrome.storage.local -setKeybinds( JSON.parse( localStorage.getItem("yt-keybinds") as string ) ) - -storage.get(["keybinds"]) -.then((result) => { - if (result.keybinds) { - // Set default keybinds if not exists - for (const [cmd, keybind] of Object.entries( DEFAULT_KEYBINDS )) { - if (!result.keybinds[cmd]) result.keybinds[cmd] = keybind - } - if (result.keybinds !== keybinds) localStorage.setItem("yt-keybinds", JSON.stringify(result.keybinds)) - setKeybinds( result.keybinds ) - } -}) - -var extraOptions = JSON.parse( localStorage.getItem("yt-extraopts") as string ) -storage.get( ["extraopts"] ) - .then((result) => { - if (result.extraopts) - { - // Set default options if not exists - for ( const [ option, value ] of Object.entries( DEFAULT_OPTIONS ) ) { - if ( result.extraopts[ option ] ) continue - result.extraopts[ option ] = value - } - - if ( result.extraopts !== extraOptions ) - localStorage.setItem("yt-extraopts", JSON.stringify(result.extraopts) ) - - extraOptions = result.extraopts - } - }) - document.addEventListener("keydown", (data) => { if ( document.activeElement === document.querySelector(`input`) || diff --git a/src/css/popup.css b/src/css/popup.css index 4671bbe..749202e 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -1,17 +1,16 @@ -@media (prefers-color-scheme: light) { - :root { - --text-primary-color: #030303; - --text-secondary-color: #888; - --bg-primary-color: #fff; - --bg-primary-hover-color: #9d9ea1; - --bg-secondary-color: #f2f2f2; - --bg-secondary-hover-color: #e6e6e6; - --separation-line-color: #e5e5e5; - --yt-brand-color: #f00; - --suggested-action-color: #378de9; - --call-to-action-color: #3ea6ff; - } +:root { + --text-primary-color: #030303; + --text-secondary-color: #888; + --bg-primary-color: #fff; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #f2f2f2; + --bg-secondary-hover-color: #e6e6e6; + --separation-line-color: #e5e5e5; + --yt-brand-color: #f00; + --suggested-action-color: #378de9; + --call-to-action-color: #3ea6ff; } + @media (prefers-color-scheme: dark) { :root { --text-primary-color: #fff; @@ -70,6 +69,15 @@ tr:not(:first-child):hover { .separation-line { background-color: var(--separation-line-color); + margin-top: 5px; + opacity: 0.9; + border: none; + outline: none; +} + +.popup_subheading { + text-align: center; + margin: 5px 0px; } svg { @@ -202,21 +210,6 @@ a { gap: 8px; } -.btn { - margin: 5px 0px; - padding: 5px 10px; - background-color: var(--suggested-action-color); - border-radius: 3px; - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275); - cursor: pointer; - transition: all 0.3s ease; - font-size: 12px; -} - -.btn:hover { - background-color: var(--call-to-action-color); -} - .modal { display: none; position: fixed; @@ -361,4 +354,83 @@ a { /* Handle on hover */ ::-webkit-scrollbar-thumb:hover { background: var(--bg-secondary-hover-color); +} + +.--page-indicator-container { + display: flex; + gap: 1rem; + width: -moz-fit-content; + width: fit-content; + margin: 0 auto; +} +.--page-indicator-container button, .--page-indicator-container input[type=submit], .--page-indicator-container input[type=reset] { + background: none; + color: inherit; + border: none; + padding: 0; + font: inherit; + cursor: pointer; + outline: inherit; +} +.--page-indicator-container .--page-indicator, +.--page-indicator-container .--page-indicator-active { + height: 1rem; + aspect-ratio: 1/1; + border-radius: 5rem; + cursor: pointer; +} +.--page-indicator-container .--page-indicator { + border: 2px solid var(--text-secondary-color); + background: transparent; +} +.--page-indicator-container .--page-indicator-active { + border: 2px solid var(--text-primary-color); + background: var(--text-primary-color); +} + +.--footer-button-container { + display: flex; + width: -moz-fit-content; + width: fit-content; + align-items: center; + justify-content: center; + gap: 1rem; + max-width: 100%; + margin: 0 auto; +} +.--footer-button-container button, .--footer-button-container input[type=submit], .--footer-button-container input[type=reset] { + background: none; + color: inherit; + border: none; + padding: 0; + font: inherit; + cursor: pointer; + outline: inherit; +} +.--footer-button-container .--footer-button { + margin: 5px 0px; + padding: 5px 10px; + background-color: transparent; + color: var(--yt-brand-color); + outline: 1px var(--yt-brand-color) solid; + border-radius: 3px; + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275); + cursor: pointer; + transition: all 0.3s ease; + font-size: 12px; +} +.--footer-button-container .--footer-button:hover { + background-color: var(--yt-brand-color); + color: var(--text-primary-color); +} + +.--global-footer { + display: flex; + width: -moz-fit-content; + width: fit-content; + max-width: 100%; + margin: 0 auto; +} +.--global-footer .--global-footer-link { + color: var(--text-secondary-color); }/*# sourceMappingURL=popup.css.map */ \ No newline at end of file diff --git a/src/css/popup.css.map b/src/css/popup.css.map index 728e609..c169958 100644 --- a/src/css/popup.css.map +++ b/src/css/popup.css.map @@ -1 +1 @@ -{"version":3,"sources":["popup.scss","popup.css"],"names":[],"mappings":"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","file":"popup.css","sourcesContent":[":root {\n\t\n}\n\n@media (prefers-color-scheme: light) \n{\n :root\n {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n }\n}\n@media (prefers-color-scheme: dark) \n{\n :root\n {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\nbody {\n\tfont-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n\tbackground: var(--bg-primary-color);\n\tcolor: var(--text-primary-color);\n scrollbar-gutter: stable;\n}\n.modal-content {\n\tbackground: var(--bg-primary-color);\n\tcolor: var(--text-primary-color);\n}\ntr:not(:first-child) {\n\tbackground-color: var(--bg-secondary-color);\n\ttransition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n\tbackground-color: var(--bg-secondary-hover-color);\n}\n#keybind-input {\n\tbackground-color: var(--bg-primary-hover-color);\n\tcaret-color: var(--text-primary-color);\n\tcolor: var(--text-primary-color);\n}\n.separation-line {\n\tbackground-color: var(--separation-line-color);\n}\n\nsvg {\n\tfill: var(--bg-primary-hover-color);\n\ttransition: all 0.5s ease;\n}\n\nsvg:hover {\n\tcursor: pointer;\n\tfill: var(--yt-brand-color) !important;\n}\n\n.container {\n\twidth: 280px;\n}\n\n.title-container {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tflex-direction: row;\n}\n\n.title {\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.version {\n\tcolor: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n\theight: 1px;\n\twidth: 100%;\n}\n\n.textbox {\n\twidth: 100%;\n\tfont-size: 12px;\n\tmargin: 0;\n}\n\nlabel {\n\tfont-size: 12px;\n}\n\n#extra_options_skip_threshold {\n\tmargin-left: 4px;\n}\n\n.textbox:focus {\n\toutline: 0;\n\tborder-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n\tdisplay: flex;\n\tflex-direction: row;\n\tjustify-content: center;\n\talign-items: center;\n\tgap: 10px;\n}\n\n.keybind-span {\n\tpadding: 3px 7px;\n\tbackground-color: var(--bg-secondary-hover-color);\n\tborder-radius: 3px;\n\tbox-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n\tcursor: default;\n}\n\n.edit-svg {\n\tfill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n\tborder-collapse: separate;\n\tborder-spacing: 0 2px;\n}\n\nth {\n\tfont-size: 12px;\n\ttext-align: center;\n}\n\ntd {\n\tfont-size: 12px;\n\tpadding: 7px 5px;\n}\n\ntd:first-child {\n\tpadding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n\tborder-bottom-left-radius: 5px;\n\tborder-top-left-radius: 5px;\n\tcursor: default;\n}\n\ntd:last-child,\nth:last-child {\n\tborder-bottom-right-radius: 5px;\n\tborder-top-right-radius: 5px;\n}\n\n.footer {\n\tfont-size: 10px;\n\ttext-align: center;\n\tmargin: 5px 0px;\n}\n\na {\n\tcolor: var(--text-primary-color);\n\ttext-decoration: none;\n\tdisplay: flex;\n}\n\n.btn-wrapper {\n\tdisplay: flex;\n\tjustify-content: center;\n\tflex-direction: row;\n\tgap: 8px;\n}\n\n.btn {\n\tmargin: 5px 0px;\n\tpadding: 5px 10px;\n\tbackground-color: var(--suggested-action-color);\n\tborder-radius: 3px;\n\tbox-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n\tcursor: pointer;\n\ttransition: all 0.3s ease;\n\tfont-size: 12px;\n}\n\n.btn:hover {\n\tbackground-color: var(--call-to-action-color);\n}\n\n.modal {\n\tdisplay: none;\n\tposition: fixed;\n\tz-index: 999;\n\tleft: 0;\n\ttop: 0;\n\twidth: 100%;\n\theight: 100%;\n\toverflow: auto;\n\tbackground-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n\tposition: absolute;\n\tleft: 50%;\n\ttop: 50%;\n\ttransform: translate(-50%, -50%);\n\tmargin: auto;\n\tpadding: 0 8px 8px 8px;\n\twidth: 80%;\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.modal-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tflex-direction: row;\n\tmargin-bottom: 4px;\n\tmargin-top: 2px;\n\talign-items: center;\n}\n\n.modal-title {\n\tfont-weight: bold;\n}\n\n.close-btn {\n\tcolor: var(--bg-primary-hover-color);\n\tfont-size: 20px;\n\tfont-weight: bold;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n\tcolor: var(--yt-brand-color);\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.input-wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: space-between;\n\talign-items: center;\n\tmargin: 10px 0px;\n\tgap: 10px;\n}\n\n.prevent-selection {\n\t-webkit-user-select: none; /* Safari */\n\t-ms-user-select: none; /* IE 10 and IE 11 */\n\tuser-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n\twidth: 30%;\n\theight: 1.5rem;\n\ttext-align: center;\n}\n\n#keybind-input:focus {\n\toutline: 0;\n\toutline: none !important;\n\tbox-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}","@media (prefers-color-scheme: light) {\n :root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n }\n}\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n scrollbar-gutter: stable;\n}\n\n.modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n#keybind-input {\n background-color: var(--bg-primary-hover-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.btn {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: var(--suggested-action-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n\n.btn:hover {\n background-color: var(--call-to-action-color);\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n margin-bottom: 4px;\n margin-top: 2px;\n align-items: center;\n}\n\n.modal-title {\n font-weight: bold;\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}"]} \ No newline at end of file +{"version":3,"sources":["popup.scss","popup.css"],"names":[],"mappings":"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","file":"popup.css","sourcesContent":[":root\n{\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n}\n\n@media (prefers-color-scheme: dark) \n{\n :root\n {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\nbody {\n\tfont-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n\tbackground: var(--bg-primary-color);\n\tcolor: var(--text-primary-color);\n scrollbar-gutter: stable;\n}\n.modal-content {\n\tbackground: var(--bg-primary-color);\n\tcolor: var(--text-primary-color);\n}\ntr:not(:first-child) {\n\tbackground-color: var(--bg-secondary-color);\n\ttransition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n\tbackground-color: var(--bg-secondary-hover-color);\n}\n#keybind-input {\n\tbackground-color: var(--bg-primary-hover-color);\n\tcaret-color: var(--text-primary-color);\n\tcolor: var(--text-primary-color);\n}\n\n// Separator\n.separation-line {\n\tbackground-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading\n{\n text-align: center;\n margin: 5px 0px\n}\n\nsvg {\n\tfill: var(--bg-primary-hover-color);\n\ttransition: all 0.5s ease;\n}\n\nsvg:hover {\n\tcursor: pointer;\n\tfill: var(--yt-brand-color) !important;\n}\n\n.container {\n\twidth: 280px;\n}\n\n.title-container {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tflex-direction: row;\n}\n\n.title {\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.version {\n\tcolor: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n\theight: 1px;\n\twidth: 100%;\n}\n\n.textbox {\n\twidth: 100%;\n\tfont-size: 12px;\n\tmargin: 0;\n}\n\nlabel {\n\tfont-size: 12px;\n}\n\n#extra_options_skip_threshold {\n\tmargin-left: 4px;\n}\n\n.textbox:focus {\n\toutline: 0;\n\tborder-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n\tdisplay: flex;\n\tflex-direction: row;\n\tjustify-content: center;\n\talign-items: center;\n\tgap: 10px;\n}\n\n.keybind-span {\n\tpadding: 3px 7px;\n\tbackground-color: var(--bg-secondary-hover-color);\n\tborder-radius: 3px;\n\tbox-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n\tcursor: default;\n}\n\n.edit-svg {\n\tfill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n\tborder-collapse: separate;\n\tborder-spacing: 0 2px;\n}\n\nth {\n\tfont-size: 12px;\n\ttext-align: center;\n}\n\ntd {\n\tfont-size: 12px;\n\tpadding: 7px 5px;\n}\n\ntd:first-child {\n\tpadding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n\tborder-bottom-left-radius: 5px;\n\tborder-top-left-radius: 5px;\n\tcursor: default;\n}\n\ntd:last-child,\nth:last-child {\n\tborder-bottom-right-radius: 5px;\n\tborder-top-right-radius: 5px;\n}\n\n.footer {\n\tfont-size: 10px;\n\ttext-align: center;\n\tmargin: 5px 0px;\n}\n\na {\n\tcolor: var(--text-primary-color);\n\ttext-decoration: none;\n\tdisplay: flex;\n}\n\n.btn-wrapper {\n\tdisplay: flex;\n\tjustify-content: center;\n\tflex-direction: row;\n\tgap: 8px;\n}\n\n.modal {\n\tdisplay: none;\n\tposition: fixed;\n\tz-index: 999;\n\tleft: 0;\n\ttop: 0;\n\twidth: 100%;\n\theight: 100%;\n\toverflow: auto;\n\tbackground-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n\tposition: absolute;\n\tleft: 50%;\n\ttop: 50%;\n\ttransform: translate(-50%, -50%);\n\tmargin: auto;\n\tpadding: 0 8px 8px 8px;\n\twidth: 80%;\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.modal-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tflex-direction: row;\n\tmargin-bottom: 4px;\n\tmargin-top: 2px;\n\talign-items: center;\n}\n\n.modal-title {\n\tfont-weight: bold;\n}\n\n.close-btn {\n\tcolor: var(--bg-primary-hover-color);\n\tfont-size: 20px;\n\tfont-weight: bold;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n\tcolor: var(--yt-brand-color);\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.input-wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: space-between;\n\talign-items: center;\n\tmargin: 10px 0px;\n\tgap: 10px;\n}\n\n.prevent-selection {\n\t-webkit-user-select: none; /* Safari */\n\t-ms-user-select: none; /* IE 10 and IE 11 */\n\tuser-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n\twidth: 30%;\n\theight: 1.5rem;\n\ttext-align: center;\n}\n\n#keybind-input:focus {\n\toutline: 0;\n\toutline: none !important;\n\tbox-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n\n.--page-indicator-container\n{\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n .--page-indicator,\n .--page-indicator-active\n {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n }\n \n // todo - change this to be an svg (fill + stroke)\n .--page-indicator\n {\n border: 2px solid var( --text-secondary-color );\n background: transparent;\n }\n .--page-indicator-active\n {\n border: 2px solid var( --text-primary-color );\n background: var( --text-primary-color );\n }\n}\n\n.--footer-button-container\n{\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n \n .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n\n background-color: transparent;\n color: var(--yt-brand-color );\n outline: 1px var( --yt-brand-color ) solid;\n\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n }\n \n .--footer-button:hover {\n background-color: var(--yt-brand-color );\n color: var(--text-primary-color);\n }\n}\n\n// this is for the github link\n.--global-footer\n{\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n\n .--global-footer-link\n {\n color: var(--text-secondary-color);\n }\n}",":root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n scrollbar-gutter: stable;\n}\n\n.modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n#keybind-input {\n background-color: var(--bg-primary-hover-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading {\n text-align: center;\n margin: 5px 0px;\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n margin-bottom: 4px;\n margin-top: 2px;\n align-items: center;\n}\n\n.modal-title {\n font-weight: bold;\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n.--page-indicator-container {\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n}\n.--page-indicator-container button, .--page-indicator-container input[type=submit], .--page-indicator-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--page-indicator-container .--page-indicator,\n.--page-indicator-container .--page-indicator-active {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n}\n.--page-indicator-container .--page-indicator {\n border: 2px solid var(--text-secondary-color);\n background: transparent;\n}\n.--page-indicator-container .--page-indicator-active {\n border: 2px solid var(--text-primary-color);\n background: var(--text-primary-color);\n}\n\n.--footer-button-container {\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n}\n.--footer-button-container button, .--footer-button-container input[type=submit], .--footer-button-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--footer-button-container .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: transparent;\n color: var(--yt-brand-color);\n outline: 1px var(--yt-brand-color) solid;\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n.--footer-button-container .--footer-button:hover {\n background-color: var(--yt-brand-color);\n color: var(--text-primary-color);\n}\n\n.--global-footer {\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n}\n.--global-footer .--global-footer-link {\n color: var(--text-secondary-color);\n}"]} \ No newline at end of file diff --git a/src/css/popup.scss b/src/css/popup.scss index 79da182..990adbe 100644 --- a/src/css/popup.scss +++ b/src/css/popup.scss @@ -1,23 +1,17 @@ -:root { - -} - -@media (prefers-color-scheme: light) +:root { - :root - { - --text-primary-color: #030303; - --text-secondary-color: #888; - --bg-primary-color: #fff; - --bg-primary-hover-color: #9d9ea1; - --bg-secondary-color: #f2f2f2; - --bg-secondary-hover-color: #e6e6e6; - --separation-line-color: #e5e5e5; - --yt-brand-color: #f00; - --suggested-action-color: #378de9; - --call-to-action-color: #3ea6ff; - } + --text-primary-color: #030303; + --text-secondary-color: #888; + --bg-primary-color: #fff; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #f2f2f2; + --bg-secondary-hover-color: #e6e6e6; + --separation-line-color: #e5e5e5; + --yt-brand-color: #f00; + --suggested-action-color: #378de9; + --call-to-action-color: #3ea6ff; } + @media (prefers-color-scheme: dark) { :root @@ -74,8 +68,20 @@ tr:not(:first-child):hover { caret-color: var(--text-primary-color); color: var(--text-primary-color); } + +// Separator .separation-line { background-color: var(--separation-line-color); + margin-top: 5px; + opacity: 0.9; + border: none; + outline: none; +} + +.popup_subheading +{ + text-align: center; + margin: 5px 0px } svg { @@ -208,21 +214,6 @@ a { gap: 8px; } -.btn { - margin: 5px 0px; - padding: 5px 10px; - background-color: var(--suggested-action-color); - border-radius: 3px; - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275); - cursor: pointer; - transition: all 0.3s ease; - font-size: 12px; -} - -.btn:hover { - background-color: var(--call-to-action-color); -} - .modal { display: none; position: fixed; @@ -374,4 +365,99 @@ a { /* Handle on hover */ ::-webkit-scrollbar-thumb:hover { background: var(--bg-secondary-hover-color); +} + + +.--page-indicator-container +{ + display: flex; + gap: 1rem; + width: fit-content; + margin: 0 auto; + + button, input[type="submit"], input[type="reset"] { + background: none; + color: inherit; + border: none; + padding: 0; + font: inherit; + cursor: pointer; + outline: inherit; + } + + .--page-indicator, + .--page-indicator-active + { + height: 1rem; + aspect-ratio: 1/1; + border-radius: 5rem; + cursor: pointer; + } + + // todo - change this to be an svg (fill + stroke) + .--page-indicator + { + border: 2px solid var( --text-secondary-color ); + background: transparent; + } + .--page-indicator-active + { + border: 2px solid var( --text-primary-color ); + background: var( --text-primary-color ); + } +} + +.--footer-button-container +{ + display: flex; + width: fit-content; + align-items: center; + justify-content: center; + gap: 1rem; + max-width: 100%; + margin: 0 auto; + + button, input[type="submit"], input[type="reset"] { + background: none; + color: inherit; + border: none; + padding: 0; + font: inherit; + cursor: pointer; + outline: inherit; + } + + .--footer-button { + margin: 5px 0px; + padding: 5px 10px; + + background-color: transparent; + color: var(--yt-brand-color ); + outline: 1px var( --yt-brand-color ) solid; + + border-radius: 3px; + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275); + cursor: pointer; + transition: all 0.3s ease; + font-size: 12px; + } + + .--footer-button:hover { + background-color: var(--yt-brand-color ); + color: var(--text-primary-color); + } +} + +// this is for the github link +.--global-footer +{ + display: flex; + width: fit-content; + max-width: 100%; + margin: 0 auto; + + .--global-footer-link + { + color: var(--text-secondary-color); + } } \ No newline at end of file diff --git a/src/lib/ResetDefaults.ts b/src/lib/ResetDefaults.ts new file mode 100644 index 0000000..77647c9 --- /dev/null +++ b/src/lib/ResetDefaults.ts @@ -0,0 +1,25 @@ +import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, setKeybinds, setOptions, storage } from "./declarations" + +/** + * Resets keybinds to their factory values in local and storage as well as the live binds + */ +export function resetKeybinds() +{ + storage.set( { "keybinds" : DEFAULT_KEYBINDS } ) + localStorage.setItem( "yt-keybinds", JSON.stringify( DEFAULT_KEYBINDS ) ) + setKeybinds( {...DEFAULT_KEYBINDS} ) + + console.log( `[BYS] :: Reset Keybinds to Defaults!` ) +} + +/** + * Resets options to their factory values in local and storage as well as the live binds + */ +export function resetOptions() +{ + storage.set( { "extraopts" : DEFAULT_OPTIONS } ) + localStorage.setItem( "yt-extraopts", JSON.stringify( DEFAULT_OPTIONS ) ) + setOptions( {...DEFAULT_OPTIONS} ) + + console.log( `[BYS] :: Reset Options to Defaults!` ) +} \ No newline at end of file diff --git a/src/lib/declarations.ts b/src/lib/declarations.ts index 5f79ee8..01723af 100644 --- a/src/lib/declarations.ts +++ b/src/lib/declarations.ts @@ -1,9 +1,9 @@ import BROWSER from "../background/browser"; -import { NumberDictionary, PolyDictionary, StateObject, StringDictionary } from "./types"; +import { DefaultsDictionary, NumberDictionary, OptionsDictionary, PolyDictionary, StateObject, StringDictionary } from "./definitions"; export const VERSION = BROWSER.runtime.getManifest().version -export const DEFAULT_KEYBINDS = { +export const DEFAULT_KEYBINDS: DefaultsDictionary = { "Seek Backward": "ArrowLeft", "Seek Forward": "ArrowRight", "Decrease Speed": "KeyU", @@ -18,16 +18,53 @@ export const DEFAULT_KEYBINDS = { "Previous Short": "KeyW", }; -export const DEFAULT_OPTIONS = { - skip_enabled: false, +export const DEFAULT_OPTIONS: DefaultsDictionary = { + // add new defaults for your option here + skip_enabled: false, skip_threshold: 500, } +export const OPTION_DICTIONARY: OptionsDictionary = { + // add details for the option (the input element type, the bounds (min/max), etc) + skip_enabled: + { + type: "checkbox", + desc: "Automatically skip shorts with fewer likes?", + }, + skip_threshold: + { + desc: "Skip shorts with fewer than this many likes:", + type: "number", + min: 0 + }, +} -export var keybinds: StringDictionary = null +export var keybinds: StringDictionary = Object.assign( {}, DEFAULT_KEYBINDS ) export const setKeybinds = ( newKeybinds: StringDictionary ) => keybinds = newKeybinds -export var options: PolyDictionary = null -export const setOptions = ( newOptions: PolyDictionary ) => keybinds = newOptions +export function setKeybind( previousState: StringDictionary, command: string, newKey: string ): StringDictionary +{ + if ( previousState === null ) return null + + const newKeybinds = {...previousState} + newKeybinds[ command ] = newKey + + return newKeybinds +} + + +export var options: PolyDictionary = Object.assign( {}, DEFAULT_OPTIONS ) +export const setOptions = ( newOptions: PolyDictionary ) => options = newOptions + +export function setOption( previousState: PolyDictionary, option: string, value: string ): StringDictionary +{ + if ( previousState === null ) return null + + const newOptions = {...previousState} + newOptions[ option ] = value + + return newOptions +} + export const storage = BROWSER.storage.local diff --git a/src/lib/types.ts b/src/lib/definitions.ts similarity index 56% rename from src/lib/types.ts rename to src/lib/definitions.ts index c7afcd5..3e4ec44 100644 --- a/src/lib/types.ts +++ b/src/lib/definitions.ts @@ -10,6 +10,19 @@ export type PolyDictionary = { [key: string]: any } | null +export type DefaultsDictionary = { + [key: string]: any +} + export interface StateObject { [key: string]: any +} + +export interface OptionsDictionary { + [key: string]: PolyDictionary +} + +export enum PopupPageNameEnum { + KEYBINDS = 0, + OPTIONS } \ No newline at end of file diff --git a/src/lib/retrieveFromStorage.ts b/src/lib/retrieveFromStorage.ts new file mode 100644 index 0000000..e9bdf13 --- /dev/null +++ b/src/lib/retrieveFromStorage.ts @@ -0,0 +1,51 @@ +import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, storage } from "./declarations" +import { PolyDictionary, StringDictionary } from "./definitions" + +export function retrieveOptionsFromStorage( setter: ( options: PolyDictionary ) => void ) +{ + const localStorageOptions = JSON.parse( localStorage.getItem("yt-extraopts") as string ) + setter( localStorageOptions ) + + storage.get( ["extraopts"] ) + .then( ( {extraopts} ) => { + if ( !extraopts ) throw Error("[BYS] :: Extra Options couldnt be loaded from storage, using defaults") + + for ( const [ option, value ] of Object.entries( DEFAULT_OPTIONS ) ) { + if ( extraopts[ option ] ) continue // * this may be an issue later on if we WANT falsy values as viable values + extraopts[ option ] = value + } + + if ( extraopts !== localStorageOptions ) + localStorage.setItem( "yt-extraopts", JSON.stringify( extraopts ) ) + + setter( extraopts ) + }) + .catch( err => { + setter( DEFAULT_OPTIONS ) + } ) +} + +export function retrieveKeybindsFromStorage( setter: ( keybinds: StringDictionary ) => void ) +{ + const localStorageKeybinds = JSON.parse( localStorage.getItem("yt-keybinds") as string ) + setter( localStorageKeybinds ) + + storage.get( ["keybinds"] ) + .then( ( {keybinds} ) => { + if ( !keybinds ) throw Error("[BYS] :: Keybinds couldnt be loaded from storage, using defaults") + + for ( const [ option, value ] of Object.entries( DEFAULT_KEYBINDS ) ) { + if ( keybinds[ option ] ) continue // * this may be an issue later on if we WANT falsy values as viable values + keybinds[ option ] = value + } + + if ( keybinds !== localStorageKeybinds ) + localStorage.setItem( "yt-keybinds", JSON.stringify( keybinds ) ) + + setter( keybinds ) + + }) + .catch( err => { + setter( DEFAULT_KEYBINDS ) + } ) +} \ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts index a1169c1..facda4f 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,4 +1,5 @@ import { NUMBER_MODIFIERS } from "./declarations" +import { PolyDictionary } from "./definitions" /** * Converts a formatted number to its full integer value. @@ -42,7 +43,7 @@ export function wheel( element: HTMLElement, codeA: () => void, codeB: () => voi element.addEventListener( "wheel", ( e: WheelEvent ) => { e.preventDefault() - if (e.deltaY > 0) codeA() + if (e.deltaY < 0) codeA() else codeB() }, { passive: false } @@ -67,4 +68,49 @@ export function render( htmlString: string ): Node if ( elements.length < 1 ) throw new Error( "ADSU | HTML String must have an element!" ) return elements[0] as Node -} \ No newline at end of file +} + +/** + * returns a standard input element type depending on the given sample value + * For example, `true` will return `"checkbox"`; `500` will return "number" + */ +export function determineInputType( sampleValue: any ): string +{ + switch( typeof sampleValue ) + { + case "boolean": + return "checkbox" + case "number": + return "number" + case "string": + return "text" + + default: + return "text" + } +} + +export function getEnumEntries( givenEnum: any ): Array<[string, any]> +{ + return Object.entries( givenEnum as Object ) + .filter( ( ( [key, val] ) => isNaN( key as any ) ) ) +} + +/** + * Assumes one word + * @param str + * @returns + */ +export function capitalise( str: string ) +{ + return str[0].toUpperCase() + str.slice( 1 ).toLowerCase() +} + + +/** + * Get enum value from key string, or return `default_return` if unfound + */ +export function getEnumWithString( givenEnum: any, key: string, default_return: any = null ) +{ + return Object.assign( {}, givenEnum )[ key ] ?? default_return +} From efd8dc2d5911d469cb1f7e10042aa5bea54ab1ef Mon Sep 17 00:00:00 2001 From: adsuth Date: Mon, 7 Aug 2023 05:08:44 +0100 Subject: [PATCH 03/60] WIP - added Keybind support --- manifest.json | 6 +- src/assets/icons/bys-128.png | Bin 8601 -> 8614 bytes src/assets/icons/bys-16.png | Bin 2317 -> 2245 bytes src/assets/icons/bys-32.png | Bin 3003 -> 2862 bytes src/assets/icons/bys-48.png | Bin 3747 -> 3586 bytes src/components/EditButton.tsx | 7 +- src/components/EditModal.tsx | 127 ++++++++++++ src/components/KeybindsTable.tsx | 28 ++- src/components/OptionsTable.tsx | 5 +- src/components/Popup.tsx | 13 +- src/content.ts | 41 +++- src/css/popup.css | 114 +++++++--- src/css/popup.css.map | 2 +- src/css/popup.scss | 344 ++++++++++++++++++++----------- src/lib/ResetDefaults.ts | 12 +- src/lib/chromeEmitters.ts | 23 +++ src/lib/declarations.ts | 48 ++++- src/lib/definitions.ts | 5 + src/lib/skipShort.ts | 4 +- src/lib/utils.ts | 10 +- 20 files changed, 596 insertions(+), 193 deletions(-) create mode 100644 src/components/EditModal.tsx create mode 100644 src/lib/chromeEmitters.ts diff --git a/manifest.json b/manifest.json index 790f7d1..6f9dd95 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,10 @@ { "manifest_version": 3, "name": "Better YouTube Shorts (BETA)", - "description": "Take back the controls on YouTube Shorts with playback, volume, progress bar and more.", - "version": "0.0.0", + "description": "Take back the controls on YouTube Shorts with playback, volume, progress bar and more!", + "version": "3.0.0", "action": { "default_popup": "index.html" }, - "permissions": ["storage"], + "permissions": [ "storage" ], "content_scripts": [ { diff --git a/src/assets/icons/bys-128.png b/src/assets/icons/bys-128.png index ac60f11b497c60d308a3d1f3191512afe4555007..15aebea48e9394a3a6be72eb7b8390aae8f285c6 100644 GIT binary patch delta 7390 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 diff --git a/src/assets/icons/bys-48.png b/src/assets/icons/bys-48.png index de5c28a380c6fd365bbd9e1d704f688850576eaf..086a8182dcd6ddd165d0bbbcde419c31e8b1f490 100644 GIT binary patch delta 2324 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 TRjk=C00000NkvXXu0mjf=8VWA diff --git a/src/components/EditButton.tsx b/src/components/EditButton.tsx index 1a81483..c32dbf5 100644 --- a/src/components/EditButton.tsx +++ b/src/components/EditButton.tsx @@ -18,8 +18,10 @@ interface Props { keybindsState: StringDictionary, setKeybindsState: any, // ! give specific type command: string, + setSelectedCommand: ( newState: string ) => void, + setIsModalOpen: ( newState: boolean ) => void } -export default function EditButton( { keybindsState, setKeybindsState, command }: Props ) { +export default function EditButton( { keybindsState, setKeybindsState, command, setSelectedCommand, setIsModalOpen }: Props ) { // todo - add on click event + props @@ -27,6 +29,9 @@ export default function EditButton( { keybindsState, setKeybindsState, command } { console.log( `Clicked: ${command}` ) + setIsModalOpen( true ) + setSelectedCommand( command ) + // setKeybindState( () => { // const newState = setKeybind( keybindState, command, key ) diff --git a/src/components/EditModal.tsx b/src/components/EditModal.tsx new file mode 100644 index 0000000..ad18096 --- /dev/null +++ b/src/components/EditModal.tsx @@ -0,0 +1,127 @@ +import { useEffect, useRef, useState } from 'react' +import Separator from './Separator' +import { DEFAULT_PRESSED_KEY, EXCLUDED_KEY_BINDS, storage } from '../lib/declarations' +import { pingChanges } from '../lib/chromeEmitters' +import { ChangedObjectStateEnum, StringDictionary } from '../lib/definitions' + + +interface Props +{ + selectedCommand: string, + isModalOpen: boolean, + setIsModalOpen: ( newState: boolean ) => void, + keybindsState: StringDictionary, + setKeybindsState: any, // ! - use proper type +} + +export default function EditModal( { selectedCommand, isModalOpen, setIsModalOpen, keybindsState, setKeybindsState }: Props ) { + + const [ inputSuccessString, setInputSuccessString ] = useState( null ) + const [ inputErrorString, setInputErrorString ] = useState( null ) + const [ pressedKey, setPressedKey ] = useState( DEFAULT_PRESSED_KEY ) // ? this is only to display this to user + + // this only exists to autofocus so inputs are immediate + const modalRef = useRef( null ) + useEffect( () => { + if ( !isModalOpen ) return + + const el = modalRef?.current as HTMLElement | null + if ( el ) + el.focus() + + }, [ isModalOpen ] ) + + useEffect( () => { + setInputErrorString( null ) + setInputSuccessString( null ) + + if ( pressedKey === DEFAULT_PRESSED_KEY ) return + if ( !canUseKey() ) setInputErrorString( `"${pressedKey}" cannot be used ` ) + else setInputSuccessString( `"${pressedKey}" can be used! Close to confirm.` ) + }, [ pressedKey ] ) + + let currentKey = "" + if ( keybindsState !== null ) + currentKey = keybindsState[ selectedCommand ] + + function handleCloseModal() + { + if ( canUseKey() ) + { + setKeybindsState( () => { + const newState = {...keybindsState} + newState[ selectedCommand ] = pressedKey + + storage.set( { "keybinds" : newState } ) + localStorage.setItem( "yt-keybinds", JSON.stringify( newState ) ) + + console.log( `[BYS] :: Bound key "${pressedKey}" to ${selectedCommand}` ) + + pingChanges( ChangedObjectStateEnum.KEYBINDS, newState ) + + return newState + } ) + } + + setIsModalOpen( false ) + + setInputErrorString( null ) + setInputSuccessString( null ) + setPressedKey( DEFAULT_PRESSED_KEY ) + } + + function handleModalInput( e: any ) // todo - change to be actual type + { + const code = e.code + setPressedKey( code ) + } + + function canUseKey() + { + console.dir( {keybindsState, pressedKey} ) + return ( + !EXCLUDED_KEY_BINDS.includes( pressedKey ) && + !Object.values( keybindsState as Object ).includes( pressedKey ) + ) + } + + function showInputInfoString() + { + // either show the success, failure or + if ( inputSuccessString ) return
{`👍 ${inputSuccessString}`}
+ if ( inputErrorString ) return
{`❌ ${inputErrorString}`}
+ + return
{"\u00A0"}
+ } + + return ( + setPressedKey( e.code )} + > +
+ + + + Edit binding for + {selectedCommand} + + + × + + + + +
+ + {pressedKey} + {showInputInfoString()} +
Does not support key combinations
+
+
+
+ ) +} diff --git a/src/components/KeybindsTable.tsx b/src/components/KeybindsTable.tsx index 28531eb..6337ae7 100644 --- a/src/components/KeybindsTable.tsx +++ b/src/components/KeybindsTable.tsx @@ -1,8 +1,9 @@ -import React from 'react' -import { keybinds } from '../lib/declarations' +import React, { useState } from 'react' +import { DEFAULT_KEYBINDS, KEYBINDS_ORDER } from '../lib/declarations' import EditButton from './EditButton' import { resetKeybinds } from '../lib/ResetDefaults' import { StringDictionary } from '../lib/definitions' +import EditModal from './EditModal' interface Props { @@ -11,21 +12,34 @@ interface Props } export default function KeybindsTable( { setKeybindsState, keybindsState }: Props ) { - + + const [ isModalOpen, setIsModalOpen ] = useState( false ) + const [ selectedCommand, setSelectedCommand ] = useState( "Seek Backward" ) + + const modalProps = { + selectedCommand, + isModalOpen, + setIsModalOpen, + keybindsState, + setKeybindsState + } + function handleResetKeybinds() { setKeybindsState( () => { resetKeybinds() - return keybinds + return DEFAULT_KEYBINDS } ) } function populateKeybindsTable() { - return Object.entries( keybindsState as Object ).map( ( [ command, bind ] ) => { + if ( keybindsState === null ) return <> + return KEYBINDS_ORDER.map( ( command: string ) => { + const bind = keybindsState[ command ] const editButtonProps = { - keybindsState, setKeybindsState, command + keybindsState, setKeybindsState, command, setSelectedCommand, setIsModalOpen } return ( @@ -47,6 +61,8 @@ export default function KeybindsTable( { setKeybindsState, keybindsState }: Prop return ( <>

Keybinds

+ + diff --git a/src/components/OptionsTable.tsx b/src/components/OptionsTable.tsx index 3156234..4ada113 100644 --- a/src/components/OptionsTable.tsx +++ b/src/components/OptionsTable.tsx @@ -1,5 +1,4 @@ -import React, { useState } from 'react' -import { DEFAULT_OPTIONS, OPTION_DICTIONARY, options, setOption, setOptions, storage } from '../lib/declarations' +import { DEFAULT_OPTIONS, OPTION_DICTIONARY, setOption, storage } from '../lib/declarations' import { determineInputType } from '../lib/utils' import { PolyDictionary } from '../lib/definitions' import { resetOptions } from '../lib/ResetDefaults' @@ -19,7 +18,7 @@ export default function OptionsTable( { optionsState, setOptionsState }: Props ) { setOptionsState( () => { resetOptions() - return options + return DEFAULT_OPTIONS } ) } diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx index 9361e42..6108100 100644 --- a/src/components/Popup.tsx +++ b/src/components/Popup.tsx @@ -5,9 +5,10 @@ import KeybindsTable from "./KeybindsTable" import OptionsTable from "./OptionsTable" import Separator from "./Separator" import PageIndicatorContainer from "./PageIndicatorContainer" -import { PolyDictionary, PopupPageNameEnum, StringDictionary } from "../lib/definitions" -import { keybinds, options } from "../lib/declarations" +import { ChangedObjectStateEnum, PolyDictionary, PopupPageNameEnum, StringDictionary } from "../lib/definitions" import { retrieveKeybindsFromStorage, retrieveOptionsFromStorage } from "../lib/retrieveFromStorage" +import { pingChanges } from "../lib/chromeEmitters" +import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS } from "../lib/declarations" // todo - split this into its component parts @@ -15,13 +16,17 @@ function Popup() { useEffect( () => { retrieveOptionsFromStorage( setOptionsState ) retrieveKeybindsFromStorage( setKeybindsState ) + + pingChanges( ChangedObjectStateEnum.KEYBINDS, keybindsState as Object ) + pingChanges( ChangedObjectStateEnum.OPTIONS, optionsState as Object ) + }, [] ) const [ currentPage, setCurrentPage ] = useState( PopupPageNameEnum.KEYBINDS ) const currentPageProps = { currentPage, setCurrentPage } - const [ keybindsState, setKeybindsState ] = useState( keybinds ) - const [ optionsState, setOptionsState ] = useState( options ) + const [ keybindsState, setKeybindsState ] = useState( DEFAULT_KEYBINDS ) + const [ optionsState, setOptionsState ] = useState( DEFAULT_OPTIONS ) const keybindsProp = { keybindsState, setKeybindsState } diff --git a/src/content.ts b/src/content.ts index 6d01c04..af54858 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,10 +1,12 @@ +import BROWSER from "./background/browser" import { handleColorScheme } from "./background/handleColorScheme" import { goToNextShort, goToPrevShort } from "./lib/changeShort" -import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, keybinds, setKeybinds, setOptions, state, storage } from "./lib/declarations" +import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, state, storage } from "./lib/declarations" +import { ChangedObjectStateEnum } from "./lib/definitions" import { getActionElement, getCurrentId, getLikeCount, getNextButton, getOverlayElement, getVideo, getVolumeContainer } from "./lib/getters" import { retrieveKeybindsFromStorage, retrieveOptionsFromStorage } from "./lib/retrieveFromStorage" import { shouldSkipShort, skipShort } from "./lib/skipShort" -import { wheel } from "./lib/utils" +import { getKeyFromEnum, wheel } from "./lib/utils" @@ -15,9 +17,21 @@ import { wheel } from "./lib/utils" * For popup code, see ./main.tsx */ -// todo - also retrieve our keybinds (move that to its own file) -retrieveOptionsFromStorage( setOptions ) -retrieveKeybindsFromStorage( setOptions ) +var keybinds = null as any +var options = null as any + +retrieveKeybindsFromStorage( newBinds => { keybinds = newBinds } ) +retrieveOptionsFromStorage( newOpts => { options = newOpts } ) + +// todo - test this on firefox +BROWSER.runtime.onMessage.addListener( ( req, sender, sendResponse ) => { + if ( req?.keybinds ) + keybinds = req.keybinds + if ( req?.options ) + options = req.options + + resetMainInterval() +} ) // watch for color scheme changes window.matchMedia( "(prefers-color-scheme: dark)" ).addEventListener( "change", ({matches}) => handleColorScheme( matches ) ) @@ -29,7 +43,6 @@ document.addEventListener("keydown", (data) => { ) return // Avoids using keys while the user interacts with any input, like search and comment. const ytShorts = getVideo() if (!ytShorts) return - if (!keybinds) setKeybinds( DEFAULT_KEYBINDS ) const key = data.code const keyAlt = data.key.toLowerCase() // for legacy keybinds @@ -202,7 +215,9 @@ var lastTime = -1 var lastSpeed = 0 var setSpeed = 1 -const timer = setInterval(() => { +var timer = setInterval( main, 100 ) + +function main() { if (window.location.toString().indexOf("youtube.com/shorts/") < 0) return const ytShorts = getVideo() @@ -214,7 +229,7 @@ const timer = setInterval(() => { if (autoplayEnabled === null) autoplayEnabled = false - var progBarList = overlayList.children[2].children[0].children[0] + var progBarList = overlayList.children[3].children[0].children[0] progBarList.removeAttribute( "hidden" ) if ( state.topId < state.currentId ) @@ -224,7 +239,7 @@ const timer = setInterval(() => { // I'm undecided whether to use 0.5 or 1 for currentTime, as 1 isn't quite fast enough, but sometimes with 0.5, it skips a video above the minimum like count. if (ytShorts && ytShorts.currentTime > 0.5 && ytShorts.duration > 1) { - if ( shouldSkipShort( state.currentId, likeCount ) ) { + if ( shouldSkipShort( options, state.currentId, likeCount ) ) { console.log("[Better Youtube Shorts] :: Skipping short that had", likeCount, "likes") state.skippedId = currentId skipShort(ytShorts) @@ -446,4 +461,10 @@ const timer = setInterval(() => { if (currentId !== null) setVolumeSlider( ytShorts ) } // if (ytShorts) checkVolume(ytShorts) // todo - uncomment this when added -}, 100) \ No newline at end of file +} + +function resetMainInterval() +{ + clearInterval( timer ) + timer = setInterval( main, 100 ) +} \ No newline at end of file diff --git a/src/css/popup.css b/src/css/popup.css index 749202e..df29a1d 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -2,6 +2,7 @@ --text-primary-color: #030303; --text-secondary-color: #888; --bg-primary-color: #fff; + --bg-modal-color: #ffffff33; --bg-primary-hover-color: #9d9ea1; --bg-secondary-color: #f2f2f2; --bg-secondary-hover-color: #e6e6e6; @@ -9,6 +10,7 @@ --yt-brand-color: #f00; --suggested-action-color: #378de9; --call-to-action-color: #3ea6ff; + --max-height: 600px; } @media (prefers-color-scheme: dark) { @@ -16,6 +18,7 @@ --text-primary-color: #fff; --text-secondary-color: #888; --bg-primary-color: #0f0f0f; + --bg-modal-color: #0f0f0f66; --bg-primary-hover-color: #9d9ea1; --bg-secondary-color: #272727; --bg-secondary-hover-color: #3d3d3d; @@ -40,16 +43,17 @@ span) { color: var(--text-primary-color); } -body { - font-family: "Source Sans Pro", "Roboto", "Noto", "Arial", sans-serif; - background: var(--bg-primary-color); - color: var(--text-primary-color); - scrollbar-gutter: stable; +* { + margin: 0; + padding: 0; + box-sizing: border-box; } -.modal-content { +body { + font-family: "Source Sans Pro", "Roboto", "Noto", "Arial", sans-serif; background: var(--bg-primary-color); color: var(--text-primary-color); + padding: 0.5rem 1rem; } tr:not(:first-child) { @@ -61,12 +65,6 @@ tr:not(:first-child):hover { background-color: var(--bg-secondary-hover-color); } -#keybind-input { - background-color: var(--bg-primary-hover-color); - caret-color: var(--text-primary-color); - color: var(--text-primary-color); -} - .separation-line { background-color: var(--separation-line-color); margin-top: 5px; @@ -234,25 +232,13 @@ a { box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7); } -.modal-header { - display: flex; - justify-content: space-between; - flex-direction: row; - margin-bottom: 4px; - margin-top: 2px; - align-items: center; -} - -.modal-title { - font-weight: bold; -} - .close-btn { color: var(--bg-primary-hover-color); font-size: 20px; font-weight: bold; text-decoration: none; cursor: pointer; + transition: color 200ms; } .close-btn:hover, @@ -433,4 +419,82 @@ a { } .--global-footer .--global-footer-link { color: var(--text-secondary-color); +} + +.--modal { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; + background: var(--bg-modal-color); +} +.--modal .--modal-header { + display: flex; + align-items: center; + justify-content: space-between; +} +.--modal .--modal-header .--modal-header-text { + font-style: italic; + color: var(--text-secondary-color); + display: block; +} +.--modal .--modal-header .--modal-header-text .--modal-command { + font-style: normal; + display: block; + font-weight: bold; +} +.--modal .--modal-content { + background: var(--bg-primary-color); + color: var(--text-primary-color); + padding: 0.5rem; + border-radius: 0.5rem; + width: 80%; + box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color); +} +.--modal .--modal-input { + outline: none; + border: none; + border-radius: 1rem; + font-size: 1.5rem; + width: 1rem; + height: 1rem; + padding: 1rem 2rem; + background-color: var(--bg-secondary-color); + caret-color: var(--text-primary-color); + color: var(--text-primary-color); + transition: background 200ms; +} +.--modal .--modal-input:hover { + background-color: var(--bg-primary-hover-color); +} +.--modal .--modal-input:focus { + background-color: var(--bg-primary-hover-color); +} +.--modal .--modal-input-error, +.--modal .--modal-input-success { + display: flex; + align-items: center; + justify-content: center; + height: 0.25rem; +} +.--modal .--modal-input-error { + color: red; +} +.--modal .--modal-input-success { + color: lime; +} +.--modal .--modal-label { + color: var(--text-secondary-color); + font-style: italic; +} +.--modal .--modal-label span { + font-style: normal; + font-weight: bold; +} + +.key-combo-warning { + color: var(--text-secondary-color); }/*# sourceMappingURL=popup.css.map */ \ No newline at end of file diff --git a/src/css/popup.css.map b/src/css/popup.css.map index c169958..dd03ef1 100644 --- a/src/css/popup.css.map +++ b/src/css/popup.css.map @@ -1 +1 @@ -{"version":3,"sources":["popup.scss","popup.css"],"names":[],"mappings":"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","file":"popup.css","sourcesContent":[":root\n{\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n}\n\n@media (prefers-color-scheme: dark) \n{\n :root\n {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\nbody {\n\tfont-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n\tbackground: var(--bg-primary-color);\n\tcolor: var(--text-primary-color);\n scrollbar-gutter: stable;\n}\n.modal-content {\n\tbackground: var(--bg-primary-color);\n\tcolor: var(--text-primary-color);\n}\ntr:not(:first-child) {\n\tbackground-color: var(--bg-secondary-color);\n\ttransition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n\tbackground-color: var(--bg-secondary-hover-color);\n}\n#keybind-input {\n\tbackground-color: var(--bg-primary-hover-color);\n\tcaret-color: var(--text-primary-color);\n\tcolor: var(--text-primary-color);\n}\n\n// Separator\n.separation-line {\n\tbackground-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading\n{\n text-align: center;\n margin: 5px 0px\n}\n\nsvg {\n\tfill: var(--bg-primary-hover-color);\n\ttransition: all 0.5s ease;\n}\n\nsvg:hover {\n\tcursor: pointer;\n\tfill: var(--yt-brand-color) !important;\n}\n\n.container {\n\twidth: 280px;\n}\n\n.title-container {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tflex-direction: row;\n}\n\n.title {\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.version {\n\tcolor: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n\theight: 1px;\n\twidth: 100%;\n}\n\n.textbox {\n\twidth: 100%;\n\tfont-size: 12px;\n\tmargin: 0;\n}\n\nlabel {\n\tfont-size: 12px;\n}\n\n#extra_options_skip_threshold {\n\tmargin-left: 4px;\n}\n\n.textbox:focus {\n\toutline: 0;\n\tborder-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n\tdisplay: flex;\n\tflex-direction: row;\n\tjustify-content: center;\n\talign-items: center;\n\tgap: 10px;\n}\n\n.keybind-span {\n\tpadding: 3px 7px;\n\tbackground-color: var(--bg-secondary-hover-color);\n\tborder-radius: 3px;\n\tbox-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n\tcursor: default;\n}\n\n.edit-svg {\n\tfill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n\tborder-collapse: separate;\n\tborder-spacing: 0 2px;\n}\n\nth {\n\tfont-size: 12px;\n\ttext-align: center;\n}\n\ntd {\n\tfont-size: 12px;\n\tpadding: 7px 5px;\n}\n\ntd:first-child {\n\tpadding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n\tborder-bottom-left-radius: 5px;\n\tborder-top-left-radius: 5px;\n\tcursor: default;\n}\n\ntd:last-child,\nth:last-child {\n\tborder-bottom-right-radius: 5px;\n\tborder-top-right-radius: 5px;\n}\n\n.footer {\n\tfont-size: 10px;\n\ttext-align: center;\n\tmargin: 5px 0px;\n}\n\na {\n\tcolor: var(--text-primary-color);\n\ttext-decoration: none;\n\tdisplay: flex;\n}\n\n.btn-wrapper {\n\tdisplay: flex;\n\tjustify-content: center;\n\tflex-direction: row;\n\tgap: 8px;\n}\n\n.modal {\n\tdisplay: none;\n\tposition: fixed;\n\tz-index: 999;\n\tleft: 0;\n\ttop: 0;\n\twidth: 100%;\n\theight: 100%;\n\toverflow: auto;\n\tbackground-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n\tposition: absolute;\n\tleft: 50%;\n\ttop: 50%;\n\ttransform: translate(-50%, -50%);\n\tmargin: auto;\n\tpadding: 0 8px 8px 8px;\n\twidth: 80%;\n\tborder-radius: 5px;\n\tbox-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.modal-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tflex-direction: row;\n\tmargin-bottom: 4px;\n\tmargin-top: 2px;\n\talign-items: center;\n}\n\n.modal-title {\n\tfont-weight: bold;\n}\n\n.close-btn {\n\tcolor: var(--bg-primary-hover-color);\n\tfont-size: 20px;\n\tfont-weight: bold;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n\tcolor: var(--yt-brand-color);\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.input-wrapper {\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: space-between;\n\talign-items: center;\n\tmargin: 10px 0px;\n\tgap: 10px;\n}\n\n.prevent-selection {\n\t-webkit-user-select: none; /* Safari */\n\t-ms-user-select: none; /* IE 10 and IE 11 */\n\tuser-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n\twidth: 30%;\n\theight: 1.5rem;\n\ttext-align: center;\n}\n\n#keybind-input:focus {\n\toutline: 0;\n\toutline: none !important;\n\tbox-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n\n.--page-indicator-container\n{\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n .--page-indicator,\n .--page-indicator-active\n {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n }\n \n // todo - change this to be an svg (fill + stroke)\n .--page-indicator\n {\n border: 2px solid var( --text-secondary-color );\n background: transparent;\n }\n .--page-indicator-active\n {\n border: 2px solid var( --text-primary-color );\n background: var( --text-primary-color );\n }\n}\n\n.--footer-button-container\n{\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n \n .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n\n background-color: transparent;\n color: var(--yt-brand-color );\n outline: 1px var( --yt-brand-color ) solid;\n\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n }\n \n .--footer-button:hover {\n background-color: var(--yt-brand-color );\n color: var(--text-primary-color);\n }\n}\n\n// this is for the github link\n.--global-footer\n{\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n\n .--global-footer-link\n {\n color: var(--text-secondary-color);\n }\n}",":root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n scrollbar-gutter: stable;\n}\n\n.modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n#keybind-input {\n background-color: var(--bg-primary-hover-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading {\n text-align: center;\n margin: 5px 0px;\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.modal-header {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n margin-bottom: 4px;\n margin-top: 2px;\n align-items: center;\n}\n\n.modal-title {\n font-weight: bold;\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n.--page-indicator-container {\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n}\n.--page-indicator-container button, .--page-indicator-container input[type=submit], .--page-indicator-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--page-indicator-container .--page-indicator,\n.--page-indicator-container .--page-indicator-active {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n}\n.--page-indicator-container .--page-indicator {\n border: 2px solid var(--text-secondary-color);\n background: transparent;\n}\n.--page-indicator-container .--page-indicator-active {\n border: 2px solid var(--text-primary-color);\n background: var(--text-primary-color);\n}\n\n.--footer-button-container {\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n}\n.--footer-button-container button, .--footer-button-container input[type=submit], .--footer-button-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--footer-button-container .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: transparent;\n color: var(--yt-brand-color);\n outline: 1px var(--yt-brand-color) solid;\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n.--footer-button-container .--footer-button:hover {\n background-color: var(--yt-brand-color);\n color: var(--text-primary-color);\n}\n\n.--global-footer {\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n}\n.--global-footer .--global-footer-link {\n color: var(--text-secondary-color);\n}"]} \ No newline at end of file +{"version":3,"sources":["popup.scss","popup.css"],"names":[],"mappings":"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","file":"popup.css","sourcesContent":[":root\n{\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n\n --max-height: 600px; // of the chrome popup\n}\n\n@media (prefers-color-scheme: dark) \n{\n :root\n {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\n*\n{\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n// Separator\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading\n{\n text-align: center;\n margin: 5px 0px\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms; \n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n\n.--page-indicator-container\n{\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n .--page-indicator,\n .--page-indicator-active\n {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n }\n \n // todo - change this to be an svg (fill + stroke)\n .--page-indicator\n {\n border: 2px solid var( --text-secondary-color );\n background: transparent;\n }\n .--page-indicator-active\n {\n border: 2px solid var( --text-primary-color );\n background: var( --text-primary-color );\n }\n}\n\n.--footer-button-container\n{\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n \n .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n\n background-color: transparent;\n color: var(--yt-brand-color );\n outline: 1px var( --yt-brand-color ) solid;\n\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n }\n \n .--footer-button:hover {\n background-color: var(--yt-brand-color );\n color: var(--text-primary-color);\n }\n}\n\n// this is for the github link\n.--global-footer\n{\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n\n .--global-footer-link\n {\n color: var(--text-secondary-color);\n }\n}\n\n.--modal\n{\n position: absolute;\n top: 0;\n left: 0;\n\n width: 100%;\n height: 100%;\n\n align-items: center;\n justify-content: center;\n \n background: var( --bg-modal-color );\n\n .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n \n .--modal-header-text\n {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n \n .--modal-command\n {\n font-style: normal;\n display: block;\n font-weight: bold;\n }\n \n }\n \n }\n\n .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n }\n\n .--modal-input {\n\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n\n transition: background 200ms;\n }\n .--modal-input:hover\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input:focus\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input-error,\n .--modal-input-success\n {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n }\n .--modal-input-error\n {\n color: red // ! - change to more accurate\n }\n .--modal-input-success\n {\n color: lime// ! - change to more accurate\n }\n\n .--modal-label\n {\n color: var(--text-secondary-color);\n font-style: italic;\n\n span\n {\n font-style: normal;\n font-weight: bold;\n }\n }\n \n}\n\n.key-combo-warning\n{\n color: var(--text-secondary-color);\n}",":root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n --max-height: 600px;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading {\n text-align: center;\n margin: 5px 0px;\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n.--page-indicator-container {\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n}\n.--page-indicator-container button, .--page-indicator-container input[type=submit], .--page-indicator-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--page-indicator-container .--page-indicator,\n.--page-indicator-container .--page-indicator-active {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n}\n.--page-indicator-container .--page-indicator {\n border: 2px solid var(--text-secondary-color);\n background: transparent;\n}\n.--page-indicator-container .--page-indicator-active {\n border: 2px solid var(--text-primary-color);\n background: var(--text-primary-color);\n}\n\n.--footer-button-container {\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n}\n.--footer-button-container button, .--footer-button-container input[type=submit], .--footer-button-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--footer-button-container .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: transparent;\n color: var(--yt-brand-color);\n outline: 1px var(--yt-brand-color) solid;\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n.--footer-button-container .--footer-button:hover {\n background-color: var(--yt-brand-color);\n color: var(--text-primary-color);\n}\n\n.--global-footer {\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n}\n.--global-footer .--global-footer-link {\n color: var(--text-secondary-color);\n}\n\n.--modal {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n align-items: center;\n justify-content: center;\n background: var(--bg-modal-color);\n}\n.--modal .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.--modal .--modal-header .--modal-header-text {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n}\n.--modal .--modal-header .--modal-header-text .--modal-command {\n font-style: normal;\n display: block;\n font-weight: bold;\n}\n.--modal .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n}\n.--modal .--modal-input {\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n transition: background 200ms;\n}\n.--modal .--modal-input:hover {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input:focus {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input-error,\n.--modal .--modal-input-success {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n}\n.--modal .--modal-input-error {\n color: red;\n}\n.--modal .--modal-input-success {\n color: lime;\n}\n.--modal .--modal-label {\n color: var(--text-secondary-color);\n font-style: italic;\n}\n.--modal .--modal-label span {\n font-style: normal;\n font-weight: bold;\n}\n\n.key-combo-warning {\n color: var(--text-secondary-color);\n}"]} \ No newline at end of file diff --git a/src/css/popup.scss b/src/css/popup.scss index 990adbe..1d330d5 100644 --- a/src/css/popup.scss +++ b/src/css/popup.scss @@ -3,6 +3,7 @@ --text-primary-color: #030303; --text-secondary-color: #888; --bg-primary-color: #fff; + --bg-modal-color: #ffffff33; --bg-primary-hover-color: #9d9ea1; --bg-secondary-color: #f2f2f2; --bg-secondary-hover-color: #e6e6e6; @@ -10,6 +11,8 @@ --yt-brand-color: #f00; --suggested-action-color: #378de9; --call-to-action-color: #3ea6ff; + + --max-height: 600px; // of the chrome popup } @media (prefers-color-scheme: dark) @@ -19,6 +22,7 @@ --text-primary-color: #fff; --text-secondary-color: #888; --bg-primary-color: #0f0f0f; + --bg-modal-color: #0f0f0f66; --bg-primary-hover-color: #9d9ea1; --bg-secondary-color: #272727; --bg-secondary-hover-color: #3d3d3d; @@ -46,32 +50,31 @@ color: var(--text-primary-color); } -body { - font-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif; - background: var(--bg-primary-color); - color: var(--text-primary-color); - scrollbar-gutter: stable; +* +{ + margin: 0; + padding: 0; + box-sizing: border-box; } -.modal-content { - background: var(--bg-primary-color); - color: var(--text-primary-color); + +body { + font-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif; + background: var(--bg-primary-color); + color: var(--text-primary-color); + padding: 0.5rem 1rem; } + tr:not(:first-child) { - background-color: var(--bg-secondary-color); - transition: all 0.3s ease; + background-color: var(--bg-secondary-color); + transition: all 0.3s ease; } tr:not(:first-child):hover { - background-color: var(--bg-secondary-hover-color); -} -#keybind-input { - background-color: var(--bg-primary-hover-color); - caret-color: var(--text-primary-color); - color: var(--text-primary-color); + background-color: var(--bg-secondary-hover-color); } // Separator .separation-line { - background-color: var(--separation-line-color); + background-color: var(--separation-line-color); margin-top: 5px; opacity: 0.9; border: none; @@ -85,76 +88,76 @@ tr:not(:first-child):hover { } svg { - fill: var(--bg-primary-hover-color); - transition: all 0.5s ease; + fill: var(--bg-primary-hover-color); + transition: all 0.5s ease; } svg:hover { - cursor: pointer; - fill: var(--yt-brand-color) !important; + cursor: pointer; + fill: var(--yt-brand-color) !important; } .container { - width: 280px; + width: 280px; } .title-container { - display: flex; - justify-content: space-between; - flex-direction: row; + display: flex; + justify-content: space-between; + flex-direction: row; } .title { - font-size: 16px; - font-weight: bold; + font-size: 16px; + font-weight: bold; } .version { - color: var(--bg-primary-hover-color); + color: var(--bg-primary-hover-color); } .separation-line { - height: 1px; - width: 100%; + height: 1px; + width: 100%; } .textbox { - width: 100%; - font-size: 12px; - margin: 0; + width: 100%; + font-size: 12px; + margin: 0; } label { - font-size: 12px; + font-size: 12px; } #extra_options_skip_threshold { - margin-left: 4px; + margin-left: 4px; } .textbox:focus { - outline: 0; - border-color: var(--call-to-action-color); + outline: 0; + border-color: var(--call-to-action-color); } .keybind-wrapper { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - gap: 10px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 10px; } .keybind-span { - padding: 3px 7px; - background-color: var(--bg-secondary-hover-color); - border-radius: 3px; - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275); - cursor: default; + padding: 3px 7px; + background-color: var(--bg-secondary-hover-color); + border-radius: 3px; + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275); + cursor: default; } .edit-svg { - fill: var(--bg-primary-hover-color); + fill: var(--bg-primary-hover-color); } .edit-btn { @@ -164,133 +167,121 @@ label { } table { - border-collapse: separate; - border-spacing: 0 2px; + border-collapse: separate; + border-spacing: 0 2px; } th { - font-size: 12px; - text-align: center; + font-size: 12px; + text-align: center; } td { - font-size: 12px; - padding: 7px 5px; + font-size: 12px; + padding: 7px 5px; } td:first-child { - padding-left: 6px; + padding-left: 6px; } td:first-child, th:first-child { - border-bottom-left-radius: 5px; - border-top-left-radius: 5px; - cursor: default; + border-bottom-left-radius: 5px; + border-top-left-radius: 5px; + cursor: default; } td:last-child, th:last-child { - border-bottom-right-radius: 5px; - border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + border-top-right-radius: 5px; } .footer { - font-size: 10px; - text-align: center; - margin: 5px 0px; + font-size: 10px; + text-align: center; + margin: 5px 0px; } a { - color: var(--text-primary-color); - text-decoration: none; - display: flex; + color: var(--text-primary-color); + text-decoration: none; + display: flex; } .btn-wrapper { - display: flex; - justify-content: center; - flex-direction: row; - gap: 8px; + display: flex; + justify-content: center; + flex-direction: row; + gap: 8px; } .modal { - display: none; - position: fixed; - z-index: 999; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgba(0, 0, 0, 0.4); + display: none; + position: fixed; + z-index: 999; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.4); } .modal-content { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - margin: auto; - padding: 0 8px 8px 8px; - width: 80%; - border-radius: 5px; - box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7); -} - -.modal-header { - display: flex; - justify-content: space-between; - flex-direction: row; - margin-bottom: 4px; - margin-top: 2px; - align-items: center; -} - -.modal-title { - font-weight: bold; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + margin: auto; + padding: 0 8px 8px 8px; + width: 80%; + border-radius: 5px; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7); } .close-btn { - color: var(--bg-primary-hover-color); - font-size: 20px; - font-weight: bold; - text-decoration: none; - cursor: pointer; + color: var(--bg-primary-hover-color); + font-size: 20px; + font-weight: bold; + text-decoration: none; + cursor: pointer; + transition: color 200ms; } .close-btn:hover, .close-btn:focus { - color: var(--yt-brand-color); - text-decoration: none; - cursor: pointer; + color: var(--yt-brand-color); + text-decoration: none; + cursor: pointer; } .input-wrapper { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - margin: 10px 0px; - gap: 10px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + margin: 10px 0px; + gap: 10px; } .prevent-selection { - -webkit-user-select: none; /* Safari */ - -ms-user-select: none; /* IE 10 and IE 11 */ - user-select: none; /* Standard syntax */ + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ } #keybind-input { - width: 30%; - height: 1.5rem; - text-align: center; + width: 30%; + height: 1.5rem; + text-align: center; } #keybind-input:focus { - outline: 0; - outline: none !important; - box-shadow: 0 0 3px #00000020; + outline: 0; + outline: none !important; + box-shadow: 0 0 3px #00000020; } .extra_options--row @@ -460,4 +451,109 @@ a { { color: var(--text-secondary-color); } +} + +.--modal +{ + position: absolute; + top: 0; + left: 0; + + width: 100%; + height: 100%; + + align-items: center; + justify-content: center; + + background: var( --bg-modal-color ); + + .--modal-header { + display: flex; + align-items: center; + justify-content: space-between; + + .--modal-header-text + { + font-style: italic; + color: var(--text-secondary-color); + display: block; + + .--modal-command + { + font-style: normal; + display: block; + font-weight: bold; + } + + } + + } + + .--modal-content { + background: var(--bg-primary-color); + color: var(--text-primary-color); + padding: 0.5rem; + border-radius: 0.5rem; + width: 80%; + box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color); + } + + .--modal-input { + + outline: none; + border: none; + border-radius: 1rem; + font-size: 1.5rem; + width: 1rem; + height: 1rem; + padding: 1rem 2rem; + + background-color: var(--bg-secondary-color); + caret-color: var(--text-primary-color); + color: var(--text-primary-color); + + transition: background 200ms; + } + .--modal-input:hover + { + background-color: var(--bg-primary-hover-color); + } + .--modal-input:focus + { + background-color: var(--bg-primary-hover-color); + } + .--modal-input-error, + .--modal-input-success + { + display: flex; + align-items: center; + justify-content: center; + height: 0.25rem; + } + .--modal-input-error + { + color: red // ! - change to more accurate + } + .--modal-input-success + { + color: lime// ! - change to more accurate + } + + .--modal-label + { + color: var(--text-secondary-color); + font-style: italic; + + span + { + font-style: normal; + font-weight: bold; + } + } + +} + +.key-combo-warning +{ + color: var(--text-secondary-color); } \ No newline at end of file diff --git a/src/lib/ResetDefaults.ts b/src/lib/ResetDefaults.ts index 77647c9..5cd4131 100644 --- a/src/lib/ResetDefaults.ts +++ b/src/lib/ResetDefaults.ts @@ -1,4 +1,6 @@ -import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, setKeybinds, setOptions, storage } from "./declarations" +import { pingChanges } from "./chromeEmitters" +import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, storage } from "./declarations" +import { ChangedObjectStateEnum } from "./definitions" /** * Resets keybinds to their factory values in local and storage as well as the live binds @@ -7,9 +9,9 @@ export function resetKeybinds() { storage.set( { "keybinds" : DEFAULT_KEYBINDS } ) localStorage.setItem( "yt-keybinds", JSON.stringify( DEFAULT_KEYBINDS ) ) - setKeybinds( {...DEFAULT_KEYBINDS} ) - console.log( `[BYS] :: Reset Keybinds to Defaults!` ) + + pingChanges( ChangedObjectStateEnum.KEYBINDS, DEFAULT_KEYBINDS ) } /** @@ -19,7 +21,7 @@ export function resetOptions() { storage.set( { "extraopts" : DEFAULT_OPTIONS } ) localStorage.setItem( "yt-extraopts", JSON.stringify( DEFAULT_OPTIONS ) ) - setOptions( {...DEFAULT_OPTIONS} ) - console.log( `[BYS] :: Reset Options to Defaults!` ) + + pingChanges( ChangedObjectStateEnum.OPTIONS, DEFAULT_OPTIONS ) } \ No newline at end of file diff --git a/src/lib/chromeEmitters.ts b/src/lib/chromeEmitters.ts new file mode 100644 index 0000000..ca49828 --- /dev/null +++ b/src/lib/chromeEmitters.ts @@ -0,0 +1,23 @@ +import BROWSER from "../background/browser"; +import { ChangedObjectStateEnum } from "./definitions"; +import { getEnumWithString, getKeyFromEnum } from "./utils"; + +/** + * Expects an object (the keybindsState or optionsState for exmaple) + * This is to be received by the content script, allowing immediate updates to the binds/options + * @param message + */ + +export async function pingChanges( objectEnum: ChangedObjectStateEnum, message: Object ) +{ + const [tab] = await BROWSER.tabs.query({active: true, lastFocusedWindow: true}) + const key = getKeyFromEnum( ChangedObjectStateEnum, objectEnum, null ) + + const content = {} as any + content[ key ] = message + + const response = await BROWSER.tabs.sendMessage( tab.id, content ); // ! - see if this works in firefox + + // do something with response here, not outside the function + console.log( `[BYS] :: Updating Keybinds` ) +} \ No newline at end of file diff --git a/src/lib/declarations.ts b/src/lib/declarations.ts index 01723af..d3e298c 100644 --- a/src/lib/declarations.ts +++ b/src/lib/declarations.ts @@ -17,6 +17,20 @@ export const DEFAULT_KEYBINDS: DefaultsDictionary = { "Next Short": "KeyS", "Previous Short": "KeyW", }; +export const KEYBINDS_ORDER: DefaultsDictionary = [ + "Seek Backward", + "Seek Forward", + "Decrease Speed", + "Reset Speed", + "Increase Speed", + "Decrease Volume", + "Increase Volume", + "Toggle Mute", + "Next Frame", + "Previous Frame", + "Next Short", + "Previous Short", +] export const DEFAULT_OPTIONS: DefaultsDictionary = { // add new defaults for your option here @@ -38,9 +52,6 @@ export const OPTION_DICTIONARY: OptionsDictionary = { }, } -export var keybinds: StringDictionary = Object.assign( {}, DEFAULT_KEYBINDS ) -export const setKeybinds = ( newKeybinds: StringDictionary ) => keybinds = newKeybinds - export function setKeybind( previousState: StringDictionary, command: string, newKey: string ): StringDictionary { if ( previousState === null ) return null @@ -51,10 +62,6 @@ export function setKeybind( previousState: StringDictionary, command: string, ne return newKeybinds } - -export var options: PolyDictionary = Object.assign( {}, DEFAULT_OPTIONS ) -export const setOptions = ( newOptions: PolyDictionary ) => options = newOptions - export function setOption( previousState: PolyDictionary, option: string, value: string ): StringDictionary { if ( previousState === null ) return null @@ -162,4 +169,29 @@ export const NUMBER_MODIFIERS: NumberDictionary = { // Swedish "mn": 1_000_000, "t": 1_000, -} \ No newline at end of file +} + +export const EXCLUDED_KEY_BINDS = [ + 'Backspace', + 'Enter', + 'NumpadEnter', + 'Escape', + 'Tab', + 'Space', + 'PageUp', + 'PageDown', + 'ArrowUp', + 'ArrowDown', + 'F13', // printscreen + 'MetaLeft', // windows/command + 'MetaRight', + + 'ControlLeft', + 'ControlRight', + 'ShiftLeft', + 'ShiftRight', + 'AltLeft', + 'AltRight', +] + +export const DEFAULT_PRESSED_KEY = "Press a Key" \ No newline at end of file diff --git a/src/lib/definitions.ts b/src/lib/definitions.ts index 3e4ec44..de7b548 100644 --- a/src/lib/definitions.ts +++ b/src/lib/definitions.ts @@ -25,4 +25,9 @@ export interface OptionsDictionary { export enum PopupPageNameEnum { KEYBINDS = 0, OPTIONS +} + +export enum ChangedObjectStateEnum { + KEYBINDS = 0, + OPTIONS } \ No newline at end of file diff --git a/src/lib/skipShort.ts b/src/lib/skipShort.ts index 18d533b..7d5ddf1 100644 --- a/src/lib/skipShort.ts +++ b/src/lib/skipShort.ts @@ -3,10 +3,10 @@ // another => https://www.youtube.com/shorts/qe56pgRVrgE?feature=share // video with 1.5M / 1,5M => https://www.youtube.com/shorts/nKZIx1bHUbQ -import { options, state } from "./declarations" +import { state } from "./declarations" import { getNextButton, getVideo } from "./getters" -export function shouldSkipShort( currentId: string, likeCount: number ) +export function shouldSkipShort( options: any, currentId: string, likeCount: number ) { // for debugging purposes diff --git a/src/lib/utils.ts b/src/lib/utils.ts index facda4f..dee89aa 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,4 +1,4 @@ -import { NUMBER_MODIFIERS } from "./declarations" +import { EXCLUDED_KEY_BINDS, NUMBER_MODIFIERS } from "./declarations" import { PolyDictionary } from "./definitions" /** @@ -114,3 +114,11 @@ export function getEnumWithString( givenEnum: any, key: string, default_return: { return Object.assign( {}, givenEnum )[ key ] ?? default_return } +/** + * Get enum key from enum, or return `default_return` if unfound + * note: this will return TO LOWER CASE!! + */ +export function getKeyFromEnum( givenEnum: any, value: any, default_return: any = null ) +{ + return givenEnum[ value ].toLowerCase() +} \ No newline at end of file From c11e86b1000f86e9f40d5dc6a6f0ebf1be5aab12 Mon Sep 17 00:00:00 2001 From: adsuth Date: Mon, 7 Aug 2023 05:40:54 +0100 Subject: [PATCH 04/60] now compatible with FF (according to checker) --- .gitignore | 4 ++++ src/background/handleColorScheme.ts | 28 ---------------------------- src/content.ts | 3 +-- src/lib/chromeEmitters.ts | 25 ++++++++++++++----------- 4 files changed, 19 insertions(+), 41 deletions(-) delete mode 100644 src/background/handleColorScheme.ts diff --git a/.gitignore b/.gitignore index a547bf3..376f006 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ dist-ssr *.njsproj *.sln *.sw? + +# extension packing files +dist.crx +dist.pem diff --git a/src/background/handleColorScheme.ts b/src/background/handleColorScheme.ts deleted file mode 100644 index 1085b9a..0000000 --- a/src/background/handleColorScheme.ts +++ /dev/null @@ -1,28 +0,0 @@ -import BROWSER from "./browser" - -export function handleColorScheme( isDarkScheme: boolean ) -{ - console.log( `[BYS] :: processing scheme (recognised as ${ isDarkScheme ? "DARK" : "LIGHT" })` ) - - // todo - create dark/light icons and change these here. - if ( isDarkScheme ) - BROWSER.browserAction.setIcon({ - path: { - "128": `bys-128.png`, - "48": `bys-48.png`, - "32": `bys-32.png`, - "16": `bys-16.png`, - } - }) - - else - BROWSER.browserAction.setIcon({ - path: { - "128": `bys-128.png`, - "48": `bys-48.png`, - "32": `bys-32.png`, - "16": `bys-16.png`, - } - }) - -} \ No newline at end of file diff --git a/src/content.ts b/src/content.ts index af54858..644894b 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,5 +1,4 @@ import BROWSER from "./background/browser" -import { handleColorScheme } from "./background/handleColorScheme" import { goToNextShort, goToPrevShort } from "./lib/changeShort" import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, state, storage } from "./lib/declarations" import { ChangedObjectStateEnum } from "./lib/definitions" @@ -34,7 +33,7 @@ BROWSER.runtime.onMessage.addListener( ( req, sender, sendResponse ) => { } ) // watch for color scheme changes -window.matchMedia( "(prefers-color-scheme: dark)" ).addEventListener( "change", ({matches}) => handleColorScheme( matches ) ) +// window.matchMedia( "(prefers-color-scheme: dark)" ).addEventListener( "change", ({matches}) => handleColorScheme( matches ) ) document.addEventListener("keydown", (data) => { if ( diff --git a/src/lib/chromeEmitters.ts b/src/lib/chromeEmitters.ts index ca49828..7a61035 100644 --- a/src/lib/chromeEmitters.ts +++ b/src/lib/chromeEmitters.ts @@ -8,16 +8,19 @@ import { getEnumWithString, getKeyFromEnum } from "./utils"; * @param message */ -export async function pingChanges( objectEnum: ChangedObjectStateEnum, message: Object ) +export function pingChanges( objectEnum: ChangedObjectStateEnum, message: Object ) { - const [tab] = await BROWSER.tabs.query({active: true, lastFocusedWindow: true}) - const key = getKeyFromEnum( ChangedObjectStateEnum, objectEnum, null ) - - const content = {} as any - content[ key ] = message - - const response = await BROWSER.tabs.sendMessage( tab.id, content ); // ! - see if this works in firefox - - // do something with response here, not outside the function - console.log( `[BYS] :: Updating Keybinds` ) + ( async () => { + const [tab] = await BROWSER.tabs.query({active: true, lastFocusedWindow: true}) + const key = getKeyFromEnum( ChangedObjectStateEnum, objectEnum, null ) + + const content = {} as any + content[ key ] = message + + const response = await BROWSER.tabs.sendMessage( tab.id, content ); // ! - see if this works in firefox + + // do something with response here, not outside the function + console.log( `[BYS] :: Updating Keybinds` ) + } )() + .catch( err => {} ) } \ No newline at end of file From 1e6db53d7ec3b7a7a2f36feee06ba4c40c246a09 Mon Sep 17 00:00:00 2001 From: adsuth Date: Tue, 8 Aug 2023 01:36:58 +0100 Subject: [PATCH 05/60] wip --- README.md | 83 ++++++ ROADMAP.md | 37 +++ src/components/EditModal.tsx | 16 +- src/components/OptionsTable.tsx | 6 +- src/components/Popup.tsx | 2 +- src/content.ts | 401 ++++++++++------------------- src/css/popup.css.map | 1 - src/css/{popup.css => style.css} | 122 ++++++++- src/css/style.css.map | 1 + src/css/{popup.scss => style.scss} | 128 ++++++++- src/lib/PlaybackRate.ts | 25 ++ src/lib/SaveToStorage.ts | 27 ++ src/lib/VolumeSlider.ts | 64 +++++ src/lib/chromeEmitters.ts | 2 +- src/lib/declarations.ts | 19 +- src/lib/definitions.ts | 3 +- src/lib/getters.ts | 21 +- src/lib/handleKeyEvent.ts | 101 ++++++++ src/lib/retrieveFromStorage.ts | 31 ++- src/lib/skipShort.ts | 3 +- src/lib/utils.ts | 2 +- 21 files changed, 791 insertions(+), 304 deletions(-) create mode 100644 README.md create mode 100644 ROADMAP.md delete mode 100644 src/css/popup.css.map rename src/css/{popup.css => style.css} (82%) create mode 100644 src/css/style.css.map rename src/css/{popup.scss => style.scss} (81%) create mode 100644 src/lib/PlaybackRate.ts create mode 100644 src/lib/SaveToStorage.ts create mode 100644 src/lib/VolumeSlider.ts create mode 100644 src/lib/handleKeyEvent.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..83b72a7 --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +
+ +![BYS Icon](./src/assets/icons/bys-128.png) + +# Better YouTube Shorts v3 + +![Current Version](https://img.shields.io/amo/v/better-youtube-shorts?label=version) +[![Chrome Web Store users](https://img.shields.io/chrome-web-store/users/icnidlkdlledahfgejnagmhgaeijokcp?label=chrome)](https://chrome.google.com/webstore/detail/better-youtube-shorts/icnidlkdlledahfgejnagmhgaeijokcp) +[![Firefox Add-on users](https://img.shields.io/amo/users/better-youtube-shorts?label=firefox)](https://addons.mozilla.org/en-US/firefox/addon/better-youtube-shorts) +![Chrome Web Store](https://img.shields.io/chrome-web-store/rating/icnidlkdlledahfgejnagmhgaeijokcp) +![License: MIT](https://img.shields.io/github/license/ynshung/better-yt-shorts) +
+ +Control your YouTube shorts just like a normal YouTube video! Features include progress bar, seeking, playback speed, auto skip and more. You can also customize the keybinds to your liking! + +## ⚠️ Development Notice +This branch is currently a work in progress and there are countless unimplemented/broken features that have yet to be addressed. + +see the [roadmap](./ROADMAP.md) for a list of goals + +--- +### Development Guide +1. Fork the project on Github +2. Clone your fork +3. Open the working directory in the terminal +4. run `npm i` to install all dependencies (ensure [node and npm are installed](https://nodejs.org/en)) +5. Once finished, run `npm run dev` to start developing. It will also create the distribution (dist) +6. Open Chrome, and type `chrome://extensions` in the searchbar +7. Toggle `Developer Mode` with the switch at the top-right of that page +8. Drag and drop the `dist` directory into that page to load the unpacked extension + +**Note that the content script will only work on the shorts page of youtube, though the popup should work anywhere** + +**Also note that, so long as the `npm run dev` is active, the extension will automatically update and refresh** + +## Installation + +* Firefox Add-on: https://addons.mozilla.org/en-US/firefox/addon/better-youtube-shorts + +## Features +* **Progress bar** at the bottom with time and duration +* **Seeking** 5 seconds backward and forward with arrow keys +* **Auto skip** short when current one ends +* Auto skip short with likes below custom threshold (e.g. 500 likes) +* Auto open comment section on each short +* Decrease and increase **playback speed** with keys U and O +* Revert to normal speed with I or by clicking the speed button +* Control **volume** with the volume slider or with - and =, mute audio with M +* Mini timestamp and speed above the like button (can be scrolled on!) +* Navigate to next or previous short **without animation** with W and S +* Go to the next **frame** or previous frame with . and , while paused +* **Customizable** keybinds + +### Default Keybinds +| Action | Shortcut | +|----------------------|------------| +| Seek Backward (+5s) | ArrowLeft | +| Seek Forward (-5s) | ArrowRight | +| Decrease Speed | KeyU | +| Reset Speed | KeyI | +| Increase Speed | KeyO | +| Decrease Volume | Minus | +| Increase Volume | Equal | +| Toggle Mute | KeyM | +| Next Frame | Comma | +| Previous Frame | Period | +| Next Short | KeyS | +| Previous Short | KeyW | + +## Screenshots + +![image](https://user-images.githubusercontent.com/80070435/219866197-2401c0d0-2632-45ed-9152-f1024828f46f.png) +![image](https://user-images.githubusercontent.com/80070435/219866370-d1acbd50-049b-47ef-9688-19d1dc4efe91.png) +![image](https://user-images.githubusercontent.com/80070435/219866388-13770811-674d-4681-be32-c7d27f35c000.png) + +## Issues / Suggestion +If you faced any issue with the extension or any suggestion that can help to improve the extension, you may create an issue [here](https://github.com/ynshung/better-yt-shorts/issues) or if you know how to code, fork the repo, make the necessary changes and create a pull request. + +You may leave your feedback in this [Google Form](https://forms.gle/pvSiMwDeQVfwyALfA). + +## License + +MIT License diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..9939473 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,37 @@ +
+ +![BYS Icon](./src/assets/icons/bys-128.png) + +# Better YouTube Shorts v3 - Roadmap +
+ +## Current Changes and Improvements from v2 +- Options and Keybinds now take effect immediately +- Added "tabbed page" view to split up keybinds, options, and anything else in the future +- the `lib` directory contains exports for both the content and popup scripts. I recommend containing each bit of unrelated functionality in their own files. + - This excludes the `declarations.ts`, which should contain global variables used throughout the program for easy access + - All global types (including interfaces and enums) are defined in `definitions.ts` + - `utils.ts` is a file for utility functions that are generic. Basically think anything that could be transferred to a different project + - Finally, theres `getters.ts` which is for selector functions (eg *getVideo()*) +- the `components` directory is for react components. Try splitting up your TSX into reusable components if possible (its not too big of a deal if you cant mind you) + +--- +## General +- Test with Firefox (I can't seem to get it to load at the moment, but the [compatibility checker](https://www.extensiontest.com/) agrees it is a compatible extension) + +## Content Script +- Fix styling for the **autoplay button** +- Implement the seek bar +- Implement the volume slider +- Clean up code; move each element to their own script +- ⚠️ **error from recent chrome update may be unfixed** + +## Popup +- Add missing functionality from more recent main branch patches (copy from the main branch): + - Option to **auto open comments**, and appropriate logic + - Option to **change the seek amount** +- Add proper icons for the tabs see [this icon pack](https://fonts.google.com/icons) +- Tweak styling for the indicators (padding and margins look off) +- **Update logo when a new logo is decided if needed** +- Remove console logs **that aren't prefaced with "[BYS] :: "** +- Fix issue with number and text inputs on the option page losing focus on input (on change changes the state, perhaps we need to instead update on loss of focus, not change) \ No newline at end of file diff --git a/src/components/EditModal.tsx b/src/components/EditModal.tsx index ad18096..eb39b1e 100644 --- a/src/components/EditModal.tsx +++ b/src/components/EditModal.tsx @@ -1,8 +1,8 @@ import { useEffect, useRef, useState } from 'react' import Separator from './Separator' -import { DEFAULT_PRESSED_KEY, EXCLUDED_KEY_BINDS, storage } from '../lib/declarations' -import { pingChanges } from '../lib/chromeEmitters' -import { ChangedObjectStateEnum, StringDictionary } from '../lib/definitions' +import { DEFAULT_PRESSED_KEY, EXCLUDED_KEY_BINDS } from '../lib/declarations' +import { StringDictionary } from '../lib/definitions' +import { saveKeybindsToStorage } from '../lib/SaveToStorage' interface Props @@ -51,13 +51,9 @@ export default function EditModal( { selectedCommand, isModalOpen, setIsModalOpe setKeybindsState( () => { const newState = {...keybindsState} newState[ selectedCommand ] = pressedKey - - storage.set( { "keybinds" : newState } ) - localStorage.setItem( "yt-keybinds", JSON.stringify( newState ) ) + saveKeybindsToStorage( newState ) console.log( `[BYS] :: Bound key "${pressedKey}" to ${selectedCommand}` ) - - pingChanges( ChangedObjectStateEnum.KEYBINDS, newState ) return newState } ) @@ -78,10 +74,10 @@ export default function EditModal( { selectedCommand, isModalOpen, setIsModalOpe function canUseKey() { - console.dir( {keybindsState, pressedKey} ) return ( !EXCLUDED_KEY_BINDS.includes( pressedKey ) && - !Object.values( keybindsState as Object ).includes( pressedKey ) + !Object.values( keybindsState as Object ).includes( pressedKey ) && + pressedKey !== DEFAULT_PRESSED_KEY ) } diff --git a/src/components/OptionsTable.tsx b/src/components/OptionsTable.tsx index 4ada113..3e7b5a9 100644 --- a/src/components/OptionsTable.tsx +++ b/src/components/OptionsTable.tsx @@ -2,6 +2,7 @@ import { DEFAULT_OPTIONS, OPTION_DICTIONARY, setOption, storage } from '../lib/d import { determineInputType } from '../lib/utils' import { PolyDictionary } from '../lib/definitions' import { resetOptions } from '../lib/ResetDefaults' +import { saveOptionsToStorage, saveSettingsToStorage } from '../lib/SaveToStorage' interface Props { @@ -47,13 +48,10 @@ export default function OptionsTable( { optionsState, setOptionsState }: Props ) value = +target.min } - // set in storage setOptionsState( () => { const newState = setOption( optionsState, option, value ) - storage.set( { "extraopts" : newState } ) - localStorage.setItem( "yt-extraopts", JSON.stringify( newState ) ) - + saveOptionsToStorage( newState ) console.log( `[BYS] :: Set Option "${option}" to ${value}` ) return newState diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx index 6108100..c5e4c9c 100644 --- a/src/components/Popup.tsx +++ b/src/components/Popup.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react" -import "../css/popup.css" +import "../css/style.css" import Header from "./Header" import KeybindsTable from "./KeybindsTable" import OptionsTable from "./OptionsTable" diff --git a/src/content.ts b/src/content.ts index 644894b..3515c6b 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,13 +1,14 @@ import BROWSER from "./background/browser" -import { goToNextShort, goToPrevShort } from "./lib/changeShort" -import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, state, storage } from "./lib/declarations" -import { ChangedObjectStateEnum } from "./lib/definitions" -import { getActionElement, getCurrentId, getLikeCount, getNextButton, getOverlayElement, getVideo, getVolumeContainer } from "./lib/getters" -import { retrieveKeybindsFromStorage, retrieveOptionsFromStorage } from "./lib/retrieveFromStorage" +import { setPlaybackRate, setTimer } from "./lib/PlaybackRate" +import { saveSettingsToStorage } from "./lib/SaveToStorage" +import { checkVolume, setVolumeSlider } from "./lib/VolumeSlider" +import { DEFAULT_STATE } from "./lib/declarations" +import { StateObject } from "./lib/definitions" +import { getActionElement, getCurrentId, getLikeCount, getNextButton, getOverlayElement, getVideo } from "./lib/getters" +import { handleKeyEvent } from "./lib/handleKeyEvent" +import { retrieveKeybindsFromStorage, retrieveOptionsFromStorage, retrieveSettingsFromStorage } from "./lib/retrieveFromStorage" import { shouldSkipShort, skipShort } from "./lib/skipShort" -import { getKeyFromEnum, wheel } from "./lib/utils" - - +import { render, wheel } from "./lib/utils" /** * content.ts @@ -16,11 +17,25 @@ import { getKeyFromEnum, wheel } from "./lib/utils" * For popup code, see ./main.tsx */ +const state = new Proxy( DEFAULT_STATE, { + set: ( o: StateObject, prop: string, val: any ) => { + o[ prop ] = val + return true + } +} ) + var keybinds = null as any var options = null as any +var settings = null as any + +// todo - add "settings" to localstorage (merge autoplay + player volume into one) +// localStorage.getItem("yt-player-volume") !== null && JSON.parse(localStorage.getItem("yt-player-volume"))["data"]["volume"] -retrieveKeybindsFromStorage( newBinds => { keybinds = newBinds } ) -retrieveOptionsFromStorage( newOpts => { options = newOpts } ) +retrieveKeybindsFromStorage( newBinds => { keybinds = newBinds } ) + +retrieveOptionsFromStorage( newOpts => { options = newOpts } ) + +retrieveSettingsFromStorage( newSettings => { settings = newSettings } ) // todo - test this on firefox BROWSER.runtime.onMessage.addListener( ( req, sender, sendResponse ) => { @@ -29,195 +44,23 @@ BROWSER.runtime.onMessage.addListener( ( req, sender, sendResponse ) => { if ( req?.options ) options = req.options - resetMainInterval() + resetIntervals() } ) // watch for color scheme changes // window.matchMedia( "(prefers-color-scheme: dark)" ).addEventListener( "change", ({matches}) => handleColorScheme( matches ) ) -document.addEventListener("keydown", (data) => { - if ( - document.activeElement === document.querySelector(`input`) || - document.activeElement === document.querySelector("#contenteditable-root") - ) return // Avoids using keys while the user interacts with any input, like search and comment. - const ytShorts = getVideo() - if (!ytShorts) return - - const key = data.code - const keyAlt = data.key.toLowerCase() // for legacy keybinds - - let command - for ( const [cmd, keybind] of Object.entries( keybinds as Object ) ) - if ( key === keybind || keyAlt === keybind ) - command = cmd - - if (!command) return - - switch (command) { - case "Seek Backward": - ytShorts.currentTime -= 5 - break - - case "Seek Forward": - ytShorts.currentTime += 5 - break - - case "Decrease Speed": - if (ytShorts.playbackRate > 0.25) ytShorts.playbackRate -= 0.25 - break - - case "Reset Speed": - ytShorts.playbackRate = 1 - break - - case "Increase Speed": - if (ytShorts.playbackRate < 16) ytShorts.playbackRate += 0.25 - break - - case "Increase Volume": - if (ytShorts.volume <= 0.975) { - setVolume(ytShorts.volume + 0.025) - } - break - - case "Decrease Volume": - if (ytShorts.volume >= 0.025) { - setVolume(ytShorts.volume - 0.025) - } - break - - case "Toggle Mute": - if ( !state.muted ) { - state.muted = true - state.volumeState = ytShorts.volume - ytShorts.volume = 0 - } else { - state.muted = false - ytShorts.volume = state.volumeState - } - break - - case "Next Frame": - if (ytShorts.paused) { - ytShorts.currentTime -= 0.04 - } - break - - case "Previous Frame": - if (ytShorts.paused) { - ytShorts.currentTime += 0.04 - } - break - - case "Next Short": - goToNextShort( ytShorts ) - break - - case "Previous Short": - goToPrevShort( ytShorts ) - break - } - setSpeed = ytShorts.playbackRate -}) - -// todo - fix this, move this -const setTimer = ( currTime: number, duration: number ) => { - const id = getCurrentId() - if ( document.getElementById(`ytTimer${id}`) === null ) return false - - const timerElement = document.getElementById( `ytTimer${id}` ) as HTMLElement - - timerElement.innerText = `${currTime}/${duration}s` - - return true -} - -// todo - generate this using my render() util method -const setVolumeSlider = ( ytShorts: HTMLVideoElement ) => { - const id = state.id - - const volumeContainer = getVolumeContainer(id) - const slider = document.createElement("input") - - if( state.actualVolume === null ) state.actualVolume = 0.5 - - // checkVolume(ytShorts) // todo - uncomment this when added - slider.id = `volumeSliderController${id}` - slider.classList.add("volume-slider") - slider.classList.add("betterYT-volume-slider") - slider.type = "range" - slider.min = "0" - slider.max = "1" - slider.step = "0.01" - slider.setAttribute("orient", "vertical") - volumeContainer.appendChild(slider) - slider.value = state.actualVolume - - slider.addEventListener( "input", e => setVolume( (e.target).valueAsNumber ) ) - - // Prevent video from pausing/playing on click - slider.addEventListener("click", (data) => { - data.stopPropagation() - }) -} - -// todo - move this to its own lib script (probably call it volumeSlider.ts) -const setVolume = ( volume: number ) => { - const id = getCurrentId() - const volumeSliderController = document.getElementById(`volumeSliderController${id}`) as HTMLInputElement - - if ( volumeSliderController === null ) return - - volumeSliderController.value = "" + volume - - // const ytShorts = document.querySelector( - // "#shorts-player > div.html5-video-container > video" - // ) as HTMLVideo - const ytShorts = getVideo() - - if ( ytShorts === null ) return - - const volumeData = { - data: { - volume: state.volume, - muted: state.muted, - } - } - - ytShorts.volume = volume - localStorage.setItem( "yt-player-volume", JSON.stringify( volumeData ) ) -} - -// todo - do this -// const checkVolume = ( ytShorts: HTMLVideoElement ) => { -// if(localStorage.getItem("yt-player-volume") !== null && JSON.parse(localStorage.getItem("yt-player-volume"))["data"]["volume"]){ -// actualVolume = JSON.parse(localStorage.getItem("yt-player-volume"))["data"]["volume"] -// ytShorts.volume = actualVolume -// }else{ -// actualVolume = ytShorts.volume -// } -// } - -const setPlaybackRate = ( currSpeed: number ) => { - const id = getCurrentId() - const playBackElement = document.getElementById( `ytPlayback${id}` ) as HTMLElement - - if ( playBackElement === null ) return false - - playBackElement.innerText = `${currSpeed}x` - - return true -} +document.addEventListener( "keydown", e => handleKeyEvent( e, settings, keybinds, options, state ) ) var injectedItem = new Set() -var lastTime = -1 -var lastSpeed = 0 -var setSpeed = 1 +var lastTime = -1 +var lastSpeed = 0 -var timer = setInterval( main, 100 ) +var main_interval = setInterval( main, 100 ) +var volume_interval = setInterval( volumeIntervalCallback, 10 ) function main() { - if (window.location.toString().indexOf("youtube.com/shorts/") < 0) return + if ( window.location.toString().indexOf("youtube.com/shorts/") < 0 ) return const ytShorts = getVideo() var currentId = getCurrentId() @@ -238,7 +81,7 @@ function main() { // I'm undecided whether to use 0.5 or 1 for currentTime, as 1 isn't quite fast enough, but sometimes with 0.5, it skips a video above the minimum like count. if (ytShorts && ytShorts.currentTime > 0.5 && ytShorts.duration > 1) { - if ( shouldSkipShort( options, state.currentId, likeCount ) ) { + if ( shouldSkipShort( state, options, state.currentId, likeCount ) ) { console.log("[Better Youtube Shorts] :: Skipping short that had", likeCount, "likes") state.skippedId = currentId skipShort(ytShorts) @@ -265,17 +108,19 @@ function main() { if (!injectedSuccess) injectedItem.delete(currentId) lastTime = currTime } - if (currSpeed != lastSpeed) { - const setRateSuccess = setPlaybackRate(currSpeed) - if (setRateSuccess) lastSpeed = currSpeed - } - } else { + setPlaybackRate( state ) + + } + else + { lastTime = -1 lastSpeed = 0 + if (autoplayEnabled && ytShorts) ytShorts.loop = false - if (actionList) { + if (actionList) + { const betterYTContainer = document.createElement("div") betterYTContainer.id = "betterYT-container" @@ -306,6 +151,8 @@ function main() { span1.setAttribute("role", "text") ytTimer.appendChild(span1) + + // Match YT's HTML structure ytButton.appendChild(para0) ytLabel.appendChild(ytButton) @@ -317,94 +164,99 @@ function main() { actionList.insertBefore(betterYTContainer, actionList.children[1]) // Autoplay Switch - const switchContainer = document.createElement("div") - const autoplaySwitch = document.createElement("label") - autoplaySwitch.classList.add("autoplay-switch") - var checkBox = document.createElement("input") - checkBox.type = "checkbox" - checkBox.id = `autoplay-checkbox${currentId}` - checkBox.checked = autoplayEnabled - var autoplaySpan = document.createElement("span") - autoplaySpan.classList.add("autoplay-slider") - autoplaySwitch.append(checkBox, autoplaySpan) - switchContainer.appendChild(autoplaySwitch) - - actionList.insertBefore(switchContainer, actionList.children[1]) - - const autoplayTitle = document.createElement("div") - autoplayTitle.classList.add("yt-spec-button-shape-with-label__label") - var span2 = document.createElement("span") - span2.setAttribute("class", "betterYT-auto yt-core-attributed-string yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--text-alignment-center") - span2.setAttribute("role", "text") - span2.textContent = "Autoplay" - autoplayTitle.appendChild(span2) - - actionList.insertBefore(autoplayTitle, actionList.children[2]) - injectedItem.add(currentId) - - - ytShorts.playbackRate = setSpeed - setPlaybackRate(setSpeed) + const autoplaySwitch = ` +
+ +
+ ` + + actionList.insertBefore( render( autoplaySwitch ), actionList.children[1] ) + + const autoplayTitle = ` +
+ Autoplay +
+ ` + + actionList.insertBefore( render( autoplayTitle ), actionList.children[2] ) + + injectedItem.add( getCurrentId() ) + + ytShorts.playbackRate = state.playbackRate + setPlaybackRate( state ) injectedSuccess = setTimer( currTime || 0, Math.round(ytShorts.duration || 0)) - betterYTContainer.addEventListener("click",() => { + betterYTContainer.addEventListener("click", () => { ytShorts.playbackRate = 1 - setSpeed = ytShorts.playbackRate + state.playbackRate = ytShorts.playbackRate }) - checkBox.addEventListener('change', () => { - if (checkBox.checked) { + document.getElementById( `autoplay-checkbox${getCurrentId()}` )?.addEventListener('change', ( e: any ) => { + if ( e.target.checked ) + { localStorage.setItem("yt-autoplay", "true") ytShorts.loop = false - } else { + } + else + { localStorage.setItem("yt-autoplay", "false") ytShorts.loop = true } }) - // todo - clean all of this up + wheel( ytButton, - speedup, - speeddown + () => { + // speedup + const video = getVideo() + if ( video === null ) return + + if (video.playbackRate < 16) video.playbackRate += 0.25 + state.playbackRate = video.playbackRate + + }, + () => { + // speeddown + const video = getVideo() + if ( video === null ) return + + if (video.playbackRate > 0.25) video.playbackRate -= 0.25 + state.playbackRate = video.playbackRate + } ) - function speedup() - { - const video = getVideo() - if ( video === null ) return - - if (video.playbackRate < 16) video.playbackRate += 0.25 - setSpeed = video.playbackRate - } - function speeddown() - { - const video = getVideo() - if ( video === null ) return - - if (video.playbackRate > 0.25) video.playbackRate -= 0.25 - setSpeed = video.playbackRate - } - - wheel( ytTimer, forward, backward ) - function forward() - { - const video = getVideo() - if ( video !== null ) video.currentTime += 1 - } - - function backward() - { - const video = getVideo() - if ( video !== null ) video.currentTime -= 1 - } + wheel( + ytTimer, + () => { + // forward + const video = getVideo() + if ( video !== null ) video.currentTime += 1 + }, + () => { + // backward + const video = getVideo() + if ( video !== null ) video.currentTime -= 1 + } + ) } + + + + + // Progress bar // todo - move this to its own file if ( overlayList ) { - var progBarList = overlayList.children[2].children[0].children[0] + var progBarList = overlayList.children[3].children[0].children[0] var progBarBG = progBarList.children[0] var progBarPlayed = progBarList.children[1] // The red part of the progress bar @@ -457,13 +309,28 @@ function main() { ytShorts.currentTime = (x / ytShorts.clientWidth) * ytShorts.duration }) } - if (currentId !== null) setVolumeSlider( ytShorts ) + + if (currentId !== null) setVolumeSlider( state, settings ) + } - // if (ytShorts) checkVolume(ytShorts) // todo - uncomment this when added + + + +} + +function volumeIntervalCallback() +{ + if ( window.location.toString().indexOf("youtube.com/shorts/") < 0 ) return + + if ( getVideo() ) checkVolume( settings ) } -function resetMainInterval() + +function resetIntervals() { - clearInterval( timer ) - timer = setInterval( main, 100 ) + clearInterval( volume_interval ) + volume_interval = setInterval( volumeIntervalCallback, 10 ) + + clearInterval( main_interval ) + main_interval = setInterval( main, 100 ) } \ No newline at end of file diff --git a/src/css/popup.css.map b/src/css/popup.css.map deleted file mode 100644 index dd03ef1..0000000 --- a/src/css/popup.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["popup.scss","popup.css"],"names":[],"mappings":"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","file":"popup.css","sourcesContent":[":root\n{\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n\n --max-height: 600px; // of the chrome popup\n}\n\n@media (prefers-color-scheme: dark) \n{\n :root\n {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\n*\n{\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n// Separator\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading\n{\n text-align: center;\n margin: 5px 0px\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms; \n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n\n.--page-indicator-container\n{\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n .--page-indicator,\n .--page-indicator-active\n {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n }\n \n // todo - change this to be an svg (fill + stroke)\n .--page-indicator\n {\n border: 2px solid var( --text-secondary-color );\n background: transparent;\n }\n .--page-indicator-active\n {\n border: 2px solid var( --text-primary-color );\n background: var( --text-primary-color );\n }\n}\n\n.--footer-button-container\n{\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n \n .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n\n background-color: transparent;\n color: var(--yt-brand-color );\n outline: 1px var( --yt-brand-color ) solid;\n\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n }\n \n .--footer-button:hover {\n background-color: var(--yt-brand-color );\n color: var(--text-primary-color);\n }\n}\n\n// this is for the github link\n.--global-footer\n{\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n\n .--global-footer-link\n {\n color: var(--text-secondary-color);\n }\n}\n\n.--modal\n{\n position: absolute;\n top: 0;\n left: 0;\n\n width: 100%;\n height: 100%;\n\n align-items: center;\n justify-content: center;\n \n background: var( --bg-modal-color );\n\n .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n \n .--modal-header-text\n {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n \n .--modal-command\n {\n font-style: normal;\n display: block;\n font-weight: bold;\n }\n \n }\n \n }\n\n .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n }\n\n .--modal-input {\n\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n\n transition: background 200ms;\n }\n .--modal-input:hover\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input:focus\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input-error,\n .--modal-input-success\n {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n }\n .--modal-input-error\n {\n color: red // ! - change to more accurate\n }\n .--modal-input-success\n {\n color: lime// ! - change to more accurate\n }\n\n .--modal-label\n {\n color: var(--text-secondary-color);\n font-style: italic;\n\n span\n {\n font-style: normal;\n font-weight: bold;\n }\n }\n \n}\n\n.key-combo-warning\n{\n color: var(--text-secondary-color);\n}",":root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n --max-height: 600px;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading {\n text-align: center;\n margin: 5px 0px;\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n.--page-indicator-container {\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n}\n.--page-indicator-container button, .--page-indicator-container input[type=submit], .--page-indicator-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--page-indicator-container .--page-indicator,\n.--page-indicator-container .--page-indicator-active {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n}\n.--page-indicator-container .--page-indicator {\n border: 2px solid var(--text-secondary-color);\n background: transparent;\n}\n.--page-indicator-container .--page-indicator-active {\n border: 2px solid var(--text-primary-color);\n background: var(--text-primary-color);\n}\n\n.--footer-button-container {\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n}\n.--footer-button-container button, .--footer-button-container input[type=submit], .--footer-button-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--footer-button-container .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: transparent;\n color: var(--yt-brand-color);\n outline: 1px var(--yt-brand-color) solid;\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n.--footer-button-container .--footer-button:hover {\n background-color: var(--yt-brand-color);\n color: var(--text-primary-color);\n}\n\n.--global-footer {\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n}\n.--global-footer .--global-footer-link {\n color: var(--text-secondary-color);\n}\n\n.--modal {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n align-items: center;\n justify-content: center;\n background: var(--bg-modal-color);\n}\n.--modal .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.--modal .--modal-header .--modal-header-text {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n}\n.--modal .--modal-header .--modal-header-text .--modal-command {\n font-style: normal;\n display: block;\n font-weight: bold;\n}\n.--modal .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n}\n.--modal .--modal-input {\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n transition: background 200ms;\n}\n.--modal .--modal-input:hover {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input:focus {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input-error,\n.--modal .--modal-input-success {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n}\n.--modal .--modal-input-error {\n color: red;\n}\n.--modal .--modal-input-success {\n color: lime;\n}\n.--modal .--modal-label {\n color: var(--text-secondary-color);\n font-style: italic;\n}\n.--modal .--modal-label span {\n font-style: normal;\n font-weight: bold;\n}\n\n.key-combo-warning {\n color: var(--text-secondary-color);\n}"]} \ No newline at end of file diff --git a/src/css/popup.css b/src/css/style.css similarity index 82% rename from src/css/popup.css rename to src/css/style.css index df29a1d..56f326c 100644 --- a/src/css/popup.css +++ b/src/css/style.css @@ -497,4 +497,124 @@ a { .key-combo-warning { color: var(--text-secondary-color); -}/*# sourceMappingURL=popup.css.map */ \ No newline at end of file +} + +.betterYT { + font-size: 16px; + font-weight: bold; + text-align: center; +} + +.betterYT-renderer { + display: inline-block; +} + +.betterYT-button-shape { + display: flex; +} + +.betterYT-volume-slider { + -webkit-appearance: slider-vertical; + position: absolute; + right: -40px; + top: 40px; + opacity: 0.4; + pointer-events: all !important; + cursor: pointer; +} + +@supports (-moz-appearance: none) { + .volume-slider { + right: 16px; + } +} +.volume-slider:hover { + opacity: 1; +} + +.autoplay-switch { + position: relative; + display: inline-block; + width: 48px; + height: 27px; +} + +/* Hide default HTML checkbox */ +.autoplay-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.autoplay-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #272727; + transition: 0.4s; + border-radius: 27px; +} + +.autoplay-slider:hover { + background-color: #3f3f3f; +} + +.autoplay-slider:before { + position: absolute; + content: ""; + height: 20px; + width: 20px; + left: 3px; + bottom: 3px; + background-color: white; + transition: 0.4s; + border-radius: 50%; +} + +input:checked + .autoplay-slider { + background-color: #3f3f3f; +} + +input:checked + .autoplay-slider:before { + transform: translateX(21px); +} + +@media screen and (max-width: 599px) { + .volumeSlider { + -webkit-appearance: slider-horizontal; + right: 44px; + top: 18px; + } + .autoplay-slider { + background-color: rgba(255, 255, 255, 0.1019607843); + } + input:checked + .autoplay-slider { + background-color: rgba(255, 255, 255, 0.1647058824); + } + .betterYT-auto { + padding-bottom: 16px; + } +} +.betterYT-progress-bar { + bottom: 0; + pointer-events: auto !important; +} + +.betterYT-progress-bar-hover { + height: 10px !important; + cursor: pointer; +} + +.betterYT-timestamp-tooltip { + position: absolute; + display: none; + background-color: rgba(39, 39, 39, 0.768627451); + border-radius: 5px; + padding: 4px; + font-size: 11px; + color: white; + z-index: 50; +}/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/src/css/style.css.map b/src/css/style.css.map new file mode 100644 index 0000000..d488dba --- /dev/null +++ b/src/css/style.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["style.scss","style.css"],"names":[],"mappings":"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","file":"style.css","sourcesContent":[":root\n{\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n\n --max-height: 600px; // of the chrome popup \n}\n\n@media (prefers-color-scheme: dark) \n{\n :root\n {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\n*\n{\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n// Separator\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading\n{\n text-align: center;\n margin: 5px 0px\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms; \n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n\n.--page-indicator-container\n{\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n .--page-indicator,\n .--page-indicator-active\n {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n }\n \n // todo - change this to be an svg (fill + stroke)\n .--page-indicator\n {\n border: 2px solid var( --text-secondary-color );\n background: transparent;\n }\n .--page-indicator-active\n {\n border: 2px solid var( --text-primary-color );\n background: var( --text-primary-color );\n }\n}\n\n.--footer-button-container\n{\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n \n .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n\n background-color: transparent;\n color: var(--yt-brand-color );\n outline: 1px var( --yt-brand-color ) solid;\n\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n }\n \n .--footer-button:hover {\n background-color: var(--yt-brand-color );\n color: var(--text-primary-color);\n }\n}\n\n// this is for the github link\n.--global-footer\n{\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n\n .--global-footer-link\n {\n color: var(--text-secondary-color);\n }\n}\n\n.--modal\n{\n position: absolute;\n top: 0;\n left: 0;\n\n width: 100%;\n height: 100%;\n\n align-items: center;\n justify-content: center;\n \n background: var( --bg-modal-color );\n\n .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n \n .--modal-header-text\n {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n \n .--modal-command\n {\n font-style: normal;\n display: block;\n font-weight: bold;\n }\n \n }\n \n }\n\n .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n }\n\n .--modal-input {\n\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n\n transition: background 200ms;\n }\n .--modal-input:hover\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input:focus\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input-error,\n .--modal-input-success\n {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n }\n .--modal-input-error\n {\n color: red // ! - change to more accurate\n }\n .--modal-input-success\n {\n color: lime// ! - change to more accurate\n }\n\n .--modal-label\n {\n color: var(--text-secondary-color);\n font-style: italic;\n\n span\n {\n font-style: normal;\n font-weight: bold;\n }\n }\n \n}\n\n.key-combo-warning\n{\n color: var(--text-secondary-color);\n}\n\n.betterYT {\n font-size: 16px;\n font-weight: bold;\n text-align: center;\n}\n\n.betterYT-renderer {\n display: inline-block;\n}\n\n.betterYT-button-shape {\n display: flex;\n}\n\n.betterYT-volume-slider{\n -webkit-appearance: slider-vertical;\n position: absolute;\n right: -40px;\n top: 40px;\n opacity: 0.4;\n pointer-events: all !important;\n cursor: pointer;\n}\n\n@supports (-moz-appearance:none) { \n .volume-slider{\n right: 16px;\n }\n}\n\n.volume-slider:hover{\n opacity: 1;\n}\n\n.autoplay-switch {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 27px;\n}\n\n/* Hide default HTML checkbox */\n.autoplay-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.autoplay-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #272727;\n -webkit-transition: .4s;\n transition: .4s;\n border-radius: 27px;\n}\n\n.autoplay-slider:hover {\n background-color: #3f3f3f;\n}\n\n.autoplay-slider:before {\n position: absolute;\n content: \"\";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n -webkit-transition: .4s;\n transition: .4s;\n border-radius: 50%;\n}\n\ninput:checked + .autoplay-slider {\n background-color: #3f3f3f;\n}\n\ninput:checked + .autoplay-slider:before {\n -webkit-transform: translateX(21px);\n -ms-transform: translateX(21px);\n transform: translateX(21px);\n}\n\n@media screen and (max-width: 599px) {\n .volumeSlider {\n -webkit-appearance: slider-horizontal;\n right: 44px;\n top: 18px;\n }\n .autoplay-slider {\n background-color: #FFFFFF1A;\n }\n input:checked + .autoplay-slider {\n background-color: #FFFFFF2A;\n }\n .betterYT-auto {\n padding-bottom: 16px;\n }\n}\n\n.betterYT-progress-bar{\n bottom: 0;\n pointer-events: auto !important;\n}\n\n.betterYT-progress-bar-hover{\n height: 10px !important;\n cursor: pointer;\n}\n\n.betterYT-timestamp-tooltip{\n position: absolute;\n display: none;\n background-color: #272727c4;\n border-radius: 5px;\n padding: 4px;\n font-size: 11px;\n color: white;\n z-index: 50;\n}",":root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n --max-height: 600px;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading {\n text-align: center;\n margin: 5px 0px;\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n.--page-indicator-container {\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n}\n.--page-indicator-container button, .--page-indicator-container input[type=submit], .--page-indicator-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--page-indicator-container .--page-indicator,\n.--page-indicator-container .--page-indicator-active {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n}\n.--page-indicator-container .--page-indicator {\n border: 2px solid var(--text-secondary-color);\n background: transparent;\n}\n.--page-indicator-container .--page-indicator-active {\n border: 2px solid var(--text-primary-color);\n background: var(--text-primary-color);\n}\n\n.--footer-button-container {\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n}\n.--footer-button-container button, .--footer-button-container input[type=submit], .--footer-button-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--footer-button-container .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: transparent;\n color: var(--yt-brand-color);\n outline: 1px var(--yt-brand-color) solid;\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n.--footer-button-container .--footer-button:hover {\n background-color: var(--yt-brand-color);\n color: var(--text-primary-color);\n}\n\n.--global-footer {\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n}\n.--global-footer .--global-footer-link {\n color: var(--text-secondary-color);\n}\n\n.--modal {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n align-items: center;\n justify-content: center;\n background: var(--bg-modal-color);\n}\n.--modal .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.--modal .--modal-header .--modal-header-text {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n}\n.--modal .--modal-header .--modal-header-text .--modal-command {\n font-style: normal;\n display: block;\n font-weight: bold;\n}\n.--modal .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n}\n.--modal .--modal-input {\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n transition: background 200ms;\n}\n.--modal .--modal-input:hover {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input:focus {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input-error,\n.--modal .--modal-input-success {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n}\n.--modal .--modal-input-error {\n color: red;\n}\n.--modal .--modal-input-success {\n color: lime;\n}\n.--modal .--modal-label {\n color: var(--text-secondary-color);\n font-style: italic;\n}\n.--modal .--modal-label span {\n font-style: normal;\n font-weight: bold;\n}\n\n.key-combo-warning {\n color: var(--text-secondary-color);\n}\n\n.betterYT {\n font-size: 16px;\n font-weight: bold;\n text-align: center;\n}\n\n.betterYT-renderer {\n display: inline-block;\n}\n\n.betterYT-button-shape {\n display: flex;\n}\n\n.betterYT-volume-slider {\n -webkit-appearance: slider-vertical;\n position: absolute;\n right: -40px;\n top: 40px;\n opacity: 0.4;\n pointer-events: all !important;\n cursor: pointer;\n}\n\n@supports (-moz-appearance: none) {\n .volume-slider {\n right: 16px;\n }\n}\n.volume-slider:hover {\n opacity: 1;\n}\n\n.autoplay-switch {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 27px;\n}\n\n/* Hide default HTML checkbox */\n.autoplay-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.autoplay-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #272727;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: 27px;\n}\n\n.autoplay-slider:hover {\n background-color: #3f3f3f;\n}\n\n.autoplay-slider:before {\n position: absolute;\n content: \"\";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: 50%;\n}\n\ninput:checked + .autoplay-slider {\n background-color: #3f3f3f;\n}\n\ninput:checked + .autoplay-slider:before {\n -webkit-transform: translateX(21px);\n -ms-transform: translateX(21px);\n transform: translateX(21px);\n}\n\n@media screen and (max-width: 599px) {\n .volumeSlider {\n -webkit-appearance: slider-horizontal;\n right: 44px;\n top: 18px;\n }\n .autoplay-slider {\n background-color: rgba(255, 255, 255, 0.1019607843);\n }\n input:checked + .autoplay-slider {\n background-color: rgba(255, 255, 255, 0.1647058824);\n }\n .betterYT-auto {\n padding-bottom: 16px;\n }\n}\n.betterYT-progress-bar {\n bottom: 0;\n pointer-events: auto !important;\n}\n\n.betterYT-progress-bar-hover {\n height: 10px !important;\n cursor: pointer;\n}\n\n.betterYT-timestamp-tooltip {\n position: absolute;\n display: none;\n background-color: rgba(39, 39, 39, 0.768627451);\n border-radius: 5px;\n padding: 4px;\n font-size: 11px;\n color: white;\n z-index: 50;\n}"]} \ No newline at end of file diff --git a/src/css/popup.scss b/src/css/style.scss similarity index 81% rename from src/css/popup.scss rename to src/css/style.scss index 1d330d5..b699a57 100644 --- a/src/css/popup.scss +++ b/src/css/style.scss @@ -12,7 +12,7 @@ --suggested-action-color: #378de9; --call-to-action-color: #3ea6ff; - --max-height: 600px; // of the chrome popup + --max-height: 600px; // of the chrome popup } @media (prefers-color-scheme: dark) @@ -556,4 +556,130 @@ a { .key-combo-warning { color: var(--text-secondary-color); +} + +.betterYT { + font-size: 16px; + font-weight: bold; + text-align: center; +} + +.betterYT-renderer { + display: inline-block; +} + +.betterYT-button-shape { + display: flex; +} + +.betterYT-volume-slider{ + -webkit-appearance: slider-vertical; + position: absolute; + right: -40px; + top: 40px; + opacity: 0.4; + pointer-events: all !important; + cursor: pointer; +} + +@supports (-moz-appearance:none) { + .volume-slider{ + right: 16px; + } +} + +.volume-slider:hover{ + opacity: 1; +} + +.autoplay-switch { + position: relative; + display: inline-block; + width: 48px; + height: 27px; +} + +/* Hide default HTML checkbox */ +.autoplay-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.autoplay-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #272727; + -webkit-transition: .4s; + transition: .4s; + border-radius: 27px; +} + +.autoplay-slider:hover { + background-color: #3f3f3f; +} + +.autoplay-slider:before { + position: absolute; + content: ""; + height: 20px; + width: 20px; + left: 3px; + bottom: 3px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + border-radius: 50%; +} + +input:checked + .autoplay-slider { + background-color: #3f3f3f; +} + +input:checked + .autoplay-slider:before { + -webkit-transform: translateX(21px); + -ms-transform: translateX(21px); + transform: translateX(21px); +} + +@media screen and (max-width: 599px) { + .volumeSlider { + -webkit-appearance: slider-horizontal; + right: 44px; + top: 18px; + } + .autoplay-slider { + background-color: #FFFFFF1A; + } + input:checked + .autoplay-slider { + background-color: #FFFFFF2A; + } + .betterYT-auto { + padding-bottom: 16px; + } +} + +.betterYT-progress-bar{ + bottom: 0; + pointer-events: auto !important; +} + +.betterYT-progress-bar-hover{ + height: 10px !important; + cursor: pointer; +} + +.betterYT-timestamp-tooltip{ + position: absolute; + display: none; + background-color: #272727c4; + border-radius: 5px; + padding: 4px; + font-size: 11px; + color: white; + z-index: 50; } \ No newline at end of file diff --git a/src/lib/PlaybackRate.ts b/src/lib/PlaybackRate.ts new file mode 100644 index 0000000..652d44f --- /dev/null +++ b/src/lib/PlaybackRate.ts @@ -0,0 +1,25 @@ +import { getCurrentId, getPlaybackElement } from "./getters" + +export function setPlaybackRate( state: any ) +{ + const id = getCurrentId() + const playBackElement = getPlaybackElement() as HTMLElement + + if ( playBackElement === null ) return false + + playBackElement.innerText = `${state.playbackRate}x` + + return true +} + +export function setTimer( currTime: number, duration: number ) +{ + const id = getCurrentId() + if ( document.getElementById(`ytTimer${id}`) === null ) return false + + const timerElement = document.getElementById( `ytTimer${id}` ) as HTMLElement + + timerElement.innerText = `${currTime}/${duration}s` + + return true +} \ No newline at end of file diff --git a/src/lib/SaveToStorage.ts b/src/lib/SaveToStorage.ts new file mode 100644 index 0000000..f262d40 --- /dev/null +++ b/src/lib/SaveToStorage.ts @@ -0,0 +1,27 @@ +import { pingChanges } from "./chromeEmitters" +import { storage } from "./declarations" +import { ChangedObjectStateEnum } from "./definitions" + +export function saveSettingsToStorage( settings: any ) +{ + storage.set( { "settings" : settings } ) + localStorage.setItem( "yt-settings", JSON.stringify( settings ) ) + + pingChanges( ChangedObjectStateEnum.SETTINGS, settings ) +} + +export function saveKeybindsToStorage( keybinds: any ) +{ + storage.set( { "keybinds" : keybinds } ) + localStorage.setItem( "yt-keybinds", JSON.stringify( keybinds ) ) + + pingChanges( ChangedObjectStateEnum.KEYBINDS, keybinds ) +} + +export function saveOptionsToStorage( options: any ) +{ + storage.set( { "extraopts" : options } ) + localStorage.setItem( "yt-extraopts", JSON.stringify( options ) ) + + pingChanges( ChangedObjectStateEnum.OPTIONS, options ) +} \ No newline at end of file diff --git a/src/lib/VolumeSlider.ts b/src/lib/VolumeSlider.ts new file mode 100644 index 0000000..e85b6f2 --- /dev/null +++ b/src/lib/VolumeSlider.ts @@ -0,0 +1,64 @@ +import { saveSettingsToStorage } from "./SaveToStorage" +import { storage } from "./declarations" +import { StateObject } from "./definitions" +import { getCurrentId, getVideo, getVolumeContainer, getVolumeSliderController } from "./getters" + +export function checkVolume( settings: any ) +{ + const ytShorts = getVideo() + + if ( ytShorts === null ) return + + if ( settings?.volume !== null ) + ytShorts.volume = settings.volume + else + settings[ "volume" ] = ytShorts.volume +} + +// todo - move this to its own lib script (probably call it volumeSlider.ts) +export function setVolume( settings: any, newVolume: number ) +{ + settings[ "volume" ] = newVolume + + const volumeSliderController = getVolumeSliderController() as HTMLInputElement + if ( volumeSliderController === null ) return + + const ytShorts = getVideo() + volumeSliderController.value = "" + settings.volume + + if ( ytShorts === null ) return + + checkVolume( settings ) + + saveSettingsToStorage( settings ) + +} + +// todo - add proper type +export function setVolumeSlider( state: StateObject, settings: any ) +{ + const ytShorts = getVideo() + const id = state.id + + const volumeContainer = getVolumeContainer() + const slider = document.createElement("input") + + if( state.userVolume === null ) state.userVolume = 0.5 + + // checkVolume(ytShorts) // todo - uncomment this when added + slider.id = `volumeSliderController${id}` + slider.classList.add("volume-slider") + slider.classList.add("betterYT-volume-slider") + slider.type = "range" + slider.min = "0" + slider.max = "1" + slider.step = "0.01" + slider.setAttribute("orient", "vertical") + volumeContainer.appendChild(slider) + slider.value = state.userVolume + + slider.addEventListener( "input", (e: any) => setVolume( settings, e.target.valueAsNumber ) ) + + // Prevent video from pausing/playing on click + slider.addEventListener("click", data => data.stopPropagation() ) +} \ No newline at end of file diff --git a/src/lib/chromeEmitters.ts b/src/lib/chromeEmitters.ts index 7a61035..4129ad5 100644 --- a/src/lib/chromeEmitters.ts +++ b/src/lib/chromeEmitters.ts @@ -1,6 +1,6 @@ import BROWSER from "../background/browser"; import { ChangedObjectStateEnum } from "./definitions"; -import { getEnumWithString, getKeyFromEnum } from "./utils"; +import { getKeyFromEnum } from "./utils"; /** * Expects an object (the keybindsState or optionsState for exmaple) diff --git a/src/lib/declarations.ts b/src/lib/declarations.ts index d3e298c..55ff905 100644 --- a/src/lib/declarations.ts +++ b/src/lib/declarations.ts @@ -75,22 +75,23 @@ export function setOption( previousState: PolyDictionary, option: string, value: export const storage = BROWSER.storage.local -const stateObject = { +export const DEFAULT_STATE = { id : 0, topId : 0, volumeState : 0, + playbackRate: 1, actualVolume: null, skippedId : null, muted : false, +} as StateObject + +// ! - add settings +export const DEFAULT_SETTINGS = { + volume: 0.25, // todo - this should be .5, just doing this for the sake of my ears } -export const state = new Proxy( stateObject, { - set: ( o: StateObject, prop: string, val: any ) => { - o[ prop ] = val - return true - } -} ) + // todo - add formats from other langs (note: dont include duplicate keys)# export const NUMBER_MODIFIERS: NumberDictionary = { @@ -194,4 +195,6 @@ export const EXCLUDED_KEY_BINDS = [ 'AltRight', ] -export const DEFAULT_PRESSED_KEY = "Press a Key" \ No newline at end of file +export const DEFAULT_PRESSED_KEY = "Press a Key" + +export const VOLUME_INCREMENT_AMOUNT = 0.025 \ No newline at end of file diff --git a/src/lib/definitions.ts b/src/lib/definitions.ts index de7b548..1061f9d 100644 --- a/src/lib/definitions.ts +++ b/src/lib/definitions.ts @@ -29,5 +29,6 @@ export enum PopupPageNameEnum { export enum ChangedObjectStateEnum { KEYBINDS = 0, - OPTIONS + OPTIONS, + SETTINGS, } \ No newline at end of file diff --git a/src/lib/getters.ts b/src/lib/getters.ts index 72f202a..9118b07 100644 --- a/src/lib/getters.ts +++ b/src/lib/getters.ts @@ -55,10 +55,13 @@ export const getOverlayElement = ( id: number | null ) => `[id="${id}"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #overlay` ) as HTMLElement -export const getVolumeContainer = ( id: number | null ) => - document.querySelector( - `[id="${id}"] > #player-container > div.player-controls.style-scope.ytd-reel-video-renderer > ytd-shorts-player-controls.style-scope.ytd-reel-video-renderer` +export function getVolumeContainer() +{ + const id = getCurrentId() + return document.querySelector( + `[id="${id}"] > #player-container > div.player-controls.style-scope.ytd-reel-video-renderer > ytd-shorts-player-controls.style-scope.ytd-reel-video-renderer` ) as HTMLElement +} export const getNextButton = () => document.querySelector( @@ -69,3 +72,15 @@ export function getVideo(): HTMLVideoElement | null { return document.querySelector( "#shorts-player>div>video" ) } + +export function getPlaybackElement() +{ + const id = getCurrentId() + return document.getElementById( `ytPlayback${id}` ) +} + +export function getVolumeSliderController() +{ + const id = getCurrentId() + return document.getElementById(`volumeSliderController${id}`) +} \ No newline at end of file diff --git a/src/lib/handleKeyEvent.ts b/src/lib/handleKeyEvent.ts new file mode 100644 index 0000000..9e2a746 --- /dev/null +++ b/src/lib/handleKeyEvent.ts @@ -0,0 +1,101 @@ +import { setVolume } from "./VolumeSlider" +import { goToNextShort, goToPrevShort } from "./changeShort" +import { VOLUME_INCREMENT_AMOUNT } from "./declarations" +import { PolyDictionary, StringDictionary } from "./definitions" +import { getVideo } from "./getters" + +export function handleKeyEvent( e: KeyboardEvent, keybinds: StringDictionary, settings: any, options: PolyDictionary, state: any ) { + if ( + document.activeElement === document.querySelector(`input`) || + document.activeElement === document.querySelector("#contenteditable-root") + ) return // Avoids using keys while the user interacts with any input, like search and comment. + + const ytShorts = getVideo() + if ( !ytShorts ) return + + const key = e.code + const keyAlt = e.key.toLowerCase() // for legacy keybinds + + let command + for ( const [cmd, keybind] of Object.entries( keybinds as Object ) ) + if ( key === keybind || keyAlt === keybind ) + command = cmd + + if (!command) return + + switch (command) { + case "Seek Backward": + ytShorts.currentTime -= 5 + break + + case "Seek Forward": + ytShorts.currentTime += 5 + break + + case "Decrease Speed": + if (ytShorts.playbackRate > 0.25) ytShorts.playbackRate -= 0.25 + break + + case "Reset Speed": + ytShorts.playbackRate = 1 + break + + case "Increase Speed": + if ( ytShorts.playbackRate < 16 ) ytShorts.playbackRate += 0.25 + break + + case "Increase Volume": + if ( ytShorts.volume < 1 ) + setVolume( settings, ytShorts.volume + VOLUME_INCREMENT_AMOUNT ) + + if ( ytShorts.volume > 1 ) + ytShorts.volume = 1 + + break + + case "Decrease Volume": + if ( ytShorts.volume > 0 ) + setVolume( settings, ytShorts.volume - VOLUME_INCREMENT_AMOUNT ) + + if ( ytShorts.volume < 0 ) + ytShorts.volume = 0 + + break + + case "Toggle Mute": + if ( !state.muted ) + { + state.muted = true + state.volumeState = ytShorts.volume + ytShorts.volume = 0 + } + else + { + state.muted = false + ytShorts.volume = state.volumeState + } + break + + case "Next Frame": + if (ytShorts.paused) { + ytShorts.currentTime -= 0.04 + } + break + + case "Previous Frame": + if (ytShorts.paused) { + ytShorts.currentTime += 0.04 + } + break + + case "Next Short": + goToNextShort( ytShorts ) + break + + case "Previous Short": + goToPrevShort( ytShorts ) + break + } + + state.currentPlaybackRate = ytShorts.playbackRate +} \ No newline at end of file diff --git a/src/lib/retrieveFromStorage.ts b/src/lib/retrieveFromStorage.ts index e9bdf13..5acb07e 100644 --- a/src/lib/retrieveFromStorage.ts +++ b/src/lib/retrieveFromStorage.ts @@ -1,7 +1,7 @@ -import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, storage } from "./declarations" +import { DEFAULT_KEYBINDS, DEFAULT_OPTIONS, DEFAULT_SETTINGS, storage } from "./declarations" import { PolyDictionary, StringDictionary } from "./definitions" -export function retrieveOptionsFromStorage( setter: ( options: PolyDictionary ) => void ) +export async function retrieveOptionsFromStorage( setter: ( options: PolyDictionary ) => void ) { const localStorageOptions = JSON.parse( localStorage.getItem("yt-extraopts") as string ) setter( localStorageOptions ) @@ -25,7 +25,7 @@ export function retrieveOptionsFromStorage( setter: ( options: PolyDictionary ) } ) } -export function retrieveKeybindsFromStorage( setter: ( keybinds: StringDictionary ) => void ) +export async function retrieveKeybindsFromStorage( setter: ( keybinds: StringDictionary ) => void ) { const localStorageKeybinds = JSON.parse( localStorage.getItem("yt-keybinds") as string ) setter( localStorageKeybinds ) @@ -48,4 +48,29 @@ export function retrieveKeybindsFromStorage( setter: ( keybinds: StringDictionar .catch( err => { setter( DEFAULT_KEYBINDS ) } ) +} + +export async function retrieveSettingsFromStorage( setter: ( settings: Object ) => void ) +{ + const localStorageSettings = JSON.parse( localStorage.getItem("yt-settings") as string ) + setter( localStorageSettings ) + + storage.get( ["settings"] ) + .then( ( {settings} ) => { + if ( !settings ) throw Error("[BYS] :: Settings couldnt be loaded from storage, using defaults") + + for ( const [ option, value ] of Object.entries( DEFAULT_SETTINGS ) ) { + if ( settings[ option ] ) continue // * this may be an issue later on if we WANT falsy values as viable values + settings[ option ] = value + } + + if ( settings !== localStorageSettings ) + localStorage.setItem( "yt-settings", JSON.stringify( settings ) ) + + setter( settings ) + + }) + .catch( err => { + setter( DEFAULT_SETTINGS ) + } ) } \ No newline at end of file diff --git a/src/lib/skipShort.ts b/src/lib/skipShort.ts index 7d5ddf1..46502fd 100644 --- a/src/lib/skipShort.ts +++ b/src/lib/skipShort.ts @@ -3,10 +3,9 @@ // another => https://www.youtube.com/shorts/qe56pgRVrgE?feature=share // video with 1.5M / 1,5M => https://www.youtube.com/shorts/nKZIx1bHUbQ -import { state } from "./declarations" import { getNextButton, getVideo } from "./getters" -export function shouldSkipShort( options: any, currentId: string, likeCount: number ) +export function shouldSkipShort( state: any, options: any, currentId: string, likeCount: number ) { // for debugging purposes diff --git a/src/lib/utils.ts b/src/lib/utils.ts index dee89aa..a7754c1 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -66,7 +66,7 @@ export function render( htmlString: string ): Node if ( elements.length > 1 ) throw new Error( "ADSU | HTML String cannot have siblings!" ) if ( elements.length < 1 ) throw new Error( "ADSU | HTML String must have an element!" ) - + return elements[0] as Node } From 66ee87e7aa2a5a5f3e9a03f21730ffdf09211984 Mon Sep 17 00:00:00 2001 From: adsuth Date: Wed, 9 Aug 2023 18:52:08 +0100 Subject: [PATCH 06/60] wip, fixed css --- ROADMAP.md | 11 +- manifest-firefox.json | 26 -- .../{KeybindsTable.tsx => KeybindsPage.tsx} | 6 +- .../{OptionsTable.tsx => OptionsPage.tsx} | 16 +- src/components/Popup.tsx | 10 +- src/content.ts | 270 +++--------------- src/css/content.css | 123 ++++++++ src/css/content.css.map | 1 + src/css/content.scss | 131 +++++++++ src/css/globals.css | 29 ++ src/css/globals.css.map | 1 + src/css/globals.scss | 33 +++ src/css/{style.css => popup.css} | 32 +-- src/css/popup.css.map | 1 + src/css/{style.scss => popup.scss} | 35 +-- src/css/style.css.map | 1 - src/lib/ActionElement.ts | 143 ++++++++++ src/lib/Autoplay.ts | 0 src/lib/ProgressBar.ts | 97 +++++++ src/lib/VolumeSlider.ts | 47 +-- src/lib/declarations.ts | 5 +- src/lib/getters.ts | 18 +- src/lib/skipShort.ts | 10 +- 23 files changed, 677 insertions(+), 369 deletions(-) delete mode 100644 manifest-firefox.json rename src/components/{KeybindsTable.tsx => KeybindsPage.tsx} (93%) rename src/components/{OptionsTable.tsx => OptionsPage.tsx} (85%) create mode 100644 src/css/content.css create mode 100644 src/css/content.css.map create mode 100644 src/css/content.scss create mode 100644 src/css/globals.css create mode 100644 src/css/globals.css.map create mode 100644 src/css/globals.scss rename src/css/{style.css => popup.css} (92%) create mode 100644 src/css/popup.css.map rename src/css/{style.scss => popup.scss} (92%) delete mode 100644 src/css/style.css.map create mode 100644 src/lib/ActionElement.ts create mode 100644 src/lib/Autoplay.ts create mode 100644 src/lib/ProgressBar.ts diff --git a/ROADMAP.md b/ROADMAP.md index 9939473..6456c31 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -20,11 +20,12 @@ - Test with Firefox (I can't seem to get it to load at the moment, but the [compatibility checker](https://www.extensiontest.com/) agrees it is a compatible extension) ## Content Script -- Fix styling for the **autoplay button** -- Implement the seek bar +- ~~Separate Popup and Content CSS into their own files (prevent weird side effects)~~ +- ~~Implement the seek bar~~ - Implement the volume slider + - Save setting to storage - Clean up code; move each element to their own script -- ⚠️ **error from recent chrome update may be unfixed** +- ~~⚠️ **error from recent chrome update may be unfixed**~~ ## Popup - Add missing functionality from more recent main branch patches (copy from the main branch): @@ -32,6 +33,6 @@ - Option to **change the seek amount** - Add proper icons for the tabs see [this icon pack](https://fonts.google.com/icons) - Tweak styling for the indicators (padding and margins look off) -- **Update logo when a new logo is decided if needed** +- ⚠️ **Update logo when a new logo is decided if needed** - Remove console logs **that aren't prefaced with "[BYS] :: "** -- Fix issue with number and text inputs on the option page losing focus on input (on change changes the state, perhaps we need to instead update on loss of focus, not change) \ No newline at end of file +- ~~Fix issue with number and text inputs on the option page losing focus on input (on change changes the state, perhaps we need to instead update on loss of focus, not change)~~ \ No newline at end of file diff --git a/manifest-firefox.json b/manifest-firefox.json deleted file mode 100644 index a017d9b..0000000 --- a/manifest-firefox.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "Better YouTube Shorts", - "description": "Take back the controls on YouTube Shorts with playback, volume, progress bar and more.", - "version": "2.6.0", - "manifest_version": 2, - "permissions": ["storage"], - "browser_action": { - "default_popup": "popup.html" - }, - "background" : { - "scripts": ["background.js"] - }, - "content_scripts": [ - { - "matches": ["https://*.youtube.com/*"], - "js": ["content-script.js"], - "css": ["styles.css"] - } - ], - "icons": { - "16": "icons/byts16.png", - "32": "icons/byts32.png", - "48": "icons/byts48.png", - "128": "icons/byts128.png" - } -} \ No newline at end of file diff --git a/src/components/KeybindsTable.tsx b/src/components/KeybindsPage.tsx similarity index 93% rename from src/components/KeybindsTable.tsx rename to src/components/KeybindsPage.tsx index 6337ae7..e34b360 100644 --- a/src/components/KeybindsTable.tsx +++ b/src/components/KeybindsPage.tsx @@ -11,7 +11,7 @@ interface Props keybindsState: StringDictionary } -export default function KeybindsTable( { setKeybindsState, keybindsState }: Props ) { +export default function KeybindsPage( { setKeybindsState, keybindsState }: Props ) { const [ isModalOpen, setIsModalOpen ] = useState( false ) const [ selectedCommand, setSelectedCommand ] = useState( "Seek Backward" ) @@ -32,7 +32,7 @@ export default function KeybindsTable( { setKeybindsState, keybindsState }: Prop } ) } - function populateKeybindsTable() + function populateKeybindsPage() { if ( keybindsState === null ) return <> @@ -72,7 +72,7 @@ export default function KeybindsTable( { setKeybindsState, keybindsState }: Prop
- { populateKeybindsTable() } + { populateKeybindsPage() }
diff --git a/src/components/OptionsTable.tsx b/src/components/OptionsPage.tsx similarity index 85% rename from src/components/OptionsTable.tsx rename to src/components/OptionsPage.tsx index 3e7b5a9..6be0d0a 100644 --- a/src/components/OptionsTable.tsx +++ b/src/components/OptionsPage.tsx @@ -10,9 +10,7 @@ interface Props setOptionsState: ( options: ( previousState: PolyDictionary ) => PolyDictionary ) => void } -const TYPES_THAT_USE_ON_CHANGE = [ "checkbox", "range" ] - -export default function OptionsTable( { optionsState, setOptionsState }: Props ) { +export default function OptionsPage( { optionsState, setOptionsState }: Props ) { // this only exists to rerender on change function handleResetOptionsClick() @@ -59,9 +57,10 @@ export default function OptionsTable( { optionsState, setOptionsState }: Props ) } - function populateOptionsTable() + function populateOptionsPage() { - return Object.entries( optionsState as Object ).map( ( [option, value] ) => { + // ! - options may not be in order, will need to implement a OPTIONS_ORDER list at some point + return Object.entries( optionsState as Object ).map( ( [option, value], i ) => { if ( OPTION_DICTIONARY === null ) return <> const type = determineInputType( value ) @@ -69,7 +68,7 @@ export default function OptionsTable( { optionsState, setOptionsState }: Props ) if ( optionsState === null ) return return ( -
+
handleOptionChange( e, option ) : undefined } - onInput = { ( !TYPES_THAT_USE_ON_CHANGE.includes( type ) ) ? e => handleOptionChange( e, option ) : undefined } + onChange = { e => handleOptionChange( e, option ) } />
) @@ -95,7 +93,7 @@ export default function OptionsTable( { optionsState, setOptionsState }: Props )

Extra Options

- { populateOptionsTable() } + { populateOptionsPage() }
diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx index c5e4c9c..34ecca7 100644 --- a/src/components/Popup.tsx +++ b/src/components/Popup.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react" -import "../css/style.css" +import "../css/popup.css" import Header from "./Header" -import KeybindsTable from "./KeybindsTable" -import OptionsTable from "./OptionsTable" +import KeybindsPage from "./KeybindsPage" +import OptionsPage from "./OptionsPage" import Separator from "./Separator" import PageIndicatorContainer from "./PageIndicatorContainer" import { ChangedObjectStateEnum, PolyDictionary, PopupPageNameEnum, StringDictionary } from "../lib/definitions" @@ -34,8 +34,8 @@ function Popup() { function getCurrentPageContent() { - if ( currentPage === PopupPageNameEnum.KEYBINDS ) return - if ( currentPage === PopupPageNameEnum.OPTIONS ) return + if ( currentPage === PopupPageNameEnum.KEYBINDS ) return + if ( currentPage === PopupPageNameEnum.OPTIONS ) return } return ( diff --git a/src/content.ts b/src/content.ts index 3515c6b..bc05ec6 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,14 +1,17 @@ import BROWSER from "./background/browser" +import { populateActionElement } from "./lib/ActionElement" import { setPlaybackRate, setTimer } from "./lib/PlaybackRate" -import { saveSettingsToStorage } from "./lib/SaveToStorage" +import { modifyProgressBar } from "./lib/ProgressBar" import { checkVolume, setVolumeSlider } from "./lib/VolumeSlider" import { DEFAULT_STATE } from "./lib/declarations" import { StateObject } from "./lib/definitions" -import { getActionElement, getCurrentId, getLikeCount, getNextButton, getOverlayElement, getVideo } from "./lib/getters" +import { getCurrentId, getLikeCount, getVideo } from "./lib/getters" import { handleKeyEvent } from "./lib/handleKeyEvent" import { retrieveKeybindsFromStorage, retrieveOptionsFromStorage, retrieveSettingsFromStorage } from "./lib/retrieveFromStorage" import { shouldSkipShort, skipShort } from "./lib/skipShort" -import { render, wheel } from "./lib/utils" + +// need this to ensure css is loaded in the dist +import "./css/content.css" /** * content.ts @@ -52,7 +55,7 @@ BROWSER.runtime.onMessage.addListener( ( req, sender, sendResponse ) => { document.addEventListener( "keydown", e => handleKeyEvent( e, settings, keybinds, options, state ) ) -var injectedItem = new Set() +// var injectedItem = new Set() var lastTime = -1 var lastSpeed = 0 @@ -65,257 +68,74 @@ function main() { const ytShorts = getVideo() var currentId = getCurrentId() var likeCount = getLikeCount( currentId ) - var actionList = getActionElement( currentId ) - var overlayList = getOverlayElement( currentId ) - var autoplayEnabled = localStorage.getItem("yt-autoplay") === "true" ? true : false + // var actionList = getActionElement( currentId ) + // var overlayList = getOverlayElement() + // var autoplayEnabled = localStorage.getItem("yt-autoplay") === "true" ? true : false - if (autoplayEnabled === null) autoplayEnabled = false + // if (autoplayEnabled === null) autoplayEnabled = false // unneeded - var progBarList = overlayList.children[3].children[0].children[0] - progBarList.removeAttribute( "hidden" ) + // var progBarList = getProgressBarList() + // progBarList.removeAttribute( "hidden" ) - if ( state.topId < state.currentId ) + if ( currentId !== null && state.topId < currentId ) state.topId = currentId // video has to have been playing to skip. // I'm undecided whether to use 0.5 or 1 for currentTime, as 1 isn't quite fast enough, but sometimes with 0.5, it skips a video above the minimum like count. - if (ytShorts && ytShorts.currentTime > 0.5 && ytShorts.duration > 1) { + if ( ytShorts && ytShorts.currentTime > 0.5 && ytShorts.duration > 1 ) { if ( shouldSkipShort( state, options, state.currentId, likeCount ) ) { - console.log("[Better Youtube Shorts] :: Skipping short that had", likeCount, "likes") + console.log("[BYS] :: Skipping short that had", likeCount, "likes") state.skippedId = currentId - skipShort(ytShorts) + skipShort() } + } if ( ytShorts === null ) return - var currTime = -1 - if ( injectedItem.has( currentId ) ) { + var currTime = Math.round( ytShorts.currentTime ) - currTime = Math.round( ytShorts.currentTime ) - var currSpeed = ytShorts.playbackRate + // ? have items been added to the page? + if ( state.injectedItems.has( currentId ) ) + { + // let currSpeed = ytShorts.playbackRate - if (autoplayEnabled && ytShorts && ytShorts.currentTime >= ytShorts.duration - 0.11) { - var nextButton = getNextButton() - nextButton.click() + if ( settings.autoplay && ytShorts.currentTime >= ytShorts.duration - 0.11 ) + { + skipShort() } - if (currTime !== lastTime) { + if ( currTime !== state.lastTime ) { // Using this as a check whether the elements actually were injected on the page - var injectedSuccess = setTimer(currTime, Math.round(ytShorts.duration || 0)) + let injectedSuccess = setTimer( currTime, Math.round(ytShorts.duration || 0) ) + // If failed, retry injection during next interval - if (!injectedSuccess) injectedItem.delete(currentId) - lastTime = currTime + if (!injectedSuccess) state.injectedItems.delete( currentId ) + + state.lastTime = currTime } setPlaybackRate( state ) } + // ? if not, then add them else { - lastTime = -1 - lastSpeed = 0 - - if (autoplayEnabled && ytShorts) ytShorts.loop = false - - if (actionList) - { - - const betterYTContainer = document.createElement("div") - betterYTContainer.id = "betterYT-container" - betterYTContainer.setAttribute("class", "button-container style-scope ytd-reel-player-overlay-renderer") - - const ytdButtonRenderer = document.createElement("div") - ytdButtonRenderer.setAttribute("class", "betterYT-renderer style-scope ytd-reel-player-overlay-renderer") - - const ytButtonShape = document.createElement("div") - ytButtonShape.setAttribute("class", "betterYT-button-shape") - - const ytLabel = document.createElement("label") - ytLabel.setAttribute("class", "yt-spec-button-shape-with-label") - - const ytButton = document.createElement("button") - ytButton.setAttribute("class", "yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-l yt-spec-button-shape-next--icon-button ") - // Playback Rate - var para0 = document.createElement("p") - para0.classList.add("betterYT") - para0.id = `ytPlayback${currentId}` - - // Timer - const ytTimer = document.createElement("div") - ytTimer.classList.add("yt-spec-button-shape-with-label__label") - var span1 = document.createElement("span") - span1.setAttribute("class", "yt-core-attributed-string yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--text-alignment-center yt-core-attributed-string--word-wrapping") - span1.id = `ytTimer${currentId}` - span1.setAttribute("role", "text") - ytTimer.appendChild(span1) - - - - // Match YT's HTML structure - ytButton.appendChild(para0) - ytLabel.appendChild(ytButton) - ytLabel.appendChild(ytTimer) - ytButtonShape.appendChild(ytLabel) - ytdButtonRenderer.appendChild(ytButtonShape) - betterYTContainer.appendChild(ytdButtonRenderer) - - actionList.insertBefore(betterYTContainer, actionList.children[1]) - - // Autoplay Switch - const autoplaySwitch = ` -
- -
- ` - - actionList.insertBefore( render( autoplaySwitch ), actionList.children[1] ) - - const autoplayTitle = ` -
- Autoplay -
- ` - - actionList.insertBefore( render( autoplayTitle ), actionList.children[2] ) - - injectedItem.add( getCurrentId() ) - - ytShorts.playbackRate = state.playbackRate - setPlaybackRate( state ) - injectedSuccess = setTimer( currTime || 0, Math.round(ytShorts.duration || 0)) - - betterYTContainer.addEventListener("click", () => { - ytShorts.playbackRate = 1 - state.playbackRate = ytShorts.playbackRate - }) - - document.getElementById( `autoplay-checkbox${getCurrentId()}` )?.addEventListener('change', ( e: any ) => { - if ( e.target.checked ) - { - localStorage.setItem("yt-autoplay", "true") - ytShorts.loop = false - } - else - { - localStorage.setItem("yt-autoplay", "false") - ytShorts.loop = true - } - }) - - - wheel( - ytButton, - () => { - // speedup - const video = getVideo() - if ( video === null ) return - - if (video.playbackRate < 16) video.playbackRate += 0.25 - state.playbackRate = video.playbackRate - - }, - () => { - // speeddown - const video = getVideo() - if ( video === null ) return - - if (video.playbackRate > 0.25) video.playbackRate -= 0.25 - state.playbackRate = video.playbackRate - } - ) - - wheel( - ytTimer, - () => { - // forward - const video = getVideo() - if ( video !== null ) video.currentTime += 1 - }, - () => { - // backward - const video = getVideo() - if ( video !== null ) video.currentTime -= 1 - } - ) - - } - - - - - + state.lastTime = -1 // reset + + if ( settings.autoplay ) ytShorts.loop = false + + populateActionElement( state, settings ) + // Progress bar - // todo - move this to its own file - if ( overlayList ) - { - var progBarList = overlayList.children[3].children[0].children[0] - var progBarBG = progBarList.children[0] - var progBarPlayed = progBarList.children[1] // The red part of the progress bar - - const timestampTooltip = document.createElement("div") - timestampTooltip.classList.add("betterYT-timestamp-tooltip") - - progBarList.appendChild(timestampTooltip); - - // Styling to ensure rest of bottom overlay (shorts title/sub button) stay in place - (overlayList.children[0]).style.marginBottom = "-7px"; - (progBarList).style.height = "10px"; - (progBarList).style.paddingTop = "2px" ;// Slight padding to increase hover box - - progBarList.classList.add('betterYT-progress-bar') - progBarBG.classList.add('betterYT-progress-bar') - progBarPlayed.classList.add('betterYT-progress-bar') - - progBarList.addEventListener("mouseover", () => { - progBarBG.classList.add('betterYT-progress-bar-hover') - progBarPlayed.classList.add('betterYT-progress-bar-hover') - }) - progBarList.addEventListener("mousemove", (event) => { - let x = (event).clientX - ytShorts.getBoundingClientRect().left - // Deal with slight inaccuracies - if (x < 0) x = 0 - if (x > ytShorts.clientWidth) x = ytShorts.clientWidth - // Get timestamp and round to nearest 0.1 - let timestamp = ((x / ytShorts.clientWidth) * ytShorts.duration).toFixed(1) - timestampTooltip.textContent = `${timestamp}s` - // Ensure tooltip stays visible at edges of client - if ((x - (timestampTooltip.offsetWidth / 2)) > (ytShorts.clientWidth - timestampTooltip.offsetWidth)) { - timestampTooltip.style.left = `${ytShorts.clientWidth - timestampTooltip.offsetWidth}px` - } else if ((x - (timestampTooltip.offsetWidth / 2)) <= 0) { - timestampTooltip.style.left = "0px" - } else { - timestampTooltip.style.left = `${x - (timestampTooltip.offsetWidth / 2)}px` - } - timestampTooltip.style.top = "-20px" - timestampTooltip.style.display = 'block' - }) - progBarList.addEventListener("mouseout", () => { - progBarBG.classList.remove('betterYT-progress-bar-hover') - progBarPlayed.classList.remove('betterYT-progress-bar-hover') - timestampTooltip.style.display = 'none' - }) - progBarList.addEventListener("click", (event) => { - let x = (event).clientX - ytShorts.getBoundingClientRect().left - if (x < 0) x = 0 - if (x > ytShorts.clientWidth) x = ytShorts.clientWidth - ytShorts.currentTime = (x / ytShorts.clientWidth) * ytShorts.duration - }) - } - + modifyProgressBar() + if (currentId !== null) setVolumeSlider( state, settings ) - + + state.injectedItems.add( getCurrentId() ) } - - } function volumeIntervalCallback() @@ -325,7 +145,6 @@ function volumeIntervalCallback() if ( getVideo() ) checkVolume( settings ) } - function resetIntervals() { clearInterval( volume_interval ) @@ -333,4 +152,5 @@ function resetIntervals() clearInterval( main_interval ) main_interval = setInterval( main, 100 ) -} \ No newline at end of file +} + diff --git a/src/css/content.css b/src/css/content.css new file mode 100644 index 0000000..920f9a2 --- /dev/null +++ b/src/css/content.css @@ -0,0 +1,123 @@ +@import url("./globals.css"); +.betterYT { + font-size: 16px; + font-weight: bold; + text-align: center; +} + +.betterYT-renderer { + display: inline-block; +} + +.betterYT-button-shape { + display: flex; +} + +.betterYT-volume-slider { + -webkit-appearance: slider-vertical; + position: absolute; + right: -40px; + top: 40px; + opacity: 0.4; + pointer-events: all !important; + accent-color: var(--yt-brand-color); + cursor: pointer; +} + +@supports (-moz-appearance: none) { + .volume-slider { + right: 16px; + } +} +.volume-slider:hover { + opacity: 1; +} + +.autoplay-switch { + position: relative; + display: block; + width: 48px; + height: 27px; + margin: 0 auto; + margin-bottom: -12px; +} + +/* Hide default HTML checkbox */ +.autoplay-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.autoplay-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #272727; + transition: 0.4s; + border-radius: 27px; +} + +.autoplay-slider:hover { + background-color: #3f3f3f; +} + +.autoplay-slider:before { + position: absolute; + content: ""; + height: 20px; + width: 20px; + left: 3px; + bottom: 3px; + background-color: white; + transition: 0.4s; + border-radius: 50%; +} + +input:checked + .autoplay-slider { + background-color: #3f3f3f; +} + +input:checked + .autoplay-slider:before { + transform: translateX(21px); +} + +@media screen and (max-width: 599px) { + .volumeSlider { + -webkit-appearance: slider-horizontal; + right: 44px; + top: 18px; + } + .autoplay-slider { + background-color: rgba(255, 255, 255, 0.1019607843); + } + input:checked + .autoplay-slider { + background-color: rgba(255, 255, 255, 0.1647058824); + } + .betterYT-auto { + padding-bottom: 16px; + } +} +.betterYT-progress-bar { + bottom: 0; + pointer-events: auto !important; +} + +.betterYT-progress-bar-hover { + height: 10px !important; + cursor: pointer; +} + +.betterYT-timestamp-tooltip { + position: absolute; + display: none; + background-color: rgba(39, 39, 39, 0.768627451); + border-radius: 5px; + padding: 4px; + font-size: 11px; + color: white; + z-index: 50; +}/*# sourceMappingURL=content.css.map */ \ No newline at end of file diff --git a/src/css/content.css.map b/src/css/content.css.map new file mode 100644 index 0000000..08d675d --- /dev/null +++ b/src/css/content.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["content.scss","content.css"],"names":[],"mappings":"AAAQ,4BAAA;AAER;EACE,eAAA;EACA,iBAAA;EACA,kBAAA;ACAF;;ADGA;EACE,qBAAA;ACAF;;ADGA;EACE,aAAA;ACAF;;ADGA;EACE,mCAAA;EACA,kBAAA;EACA,YAAA;EACA,SAAA;EACA,YAAA;EACA,8BAAA;EACA,mCAAA;EACA,eAAA;ACAF;;ADGA;EACE;IACI,WAAA;ECAJ;AACF;ADGA;EACE,UAAA;ACDF;;ADIA;EACE,kBAAA;EACA,cAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,oBAAA;ACDF;;ADIA,+BAAA;AACA;EACE,UAAA;EACA,QAAA;EACA,SAAA;ACDF;;ADIA;EACE,kBAAA;EACA,eAAA;EACA,MAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;EACA,yBAAA;EAEA,gBAAA;EACA,mBAAA;ACDF;;ADKA;EACE,yBAAA;ACFF;;ADKA;EACE,kBAAA;EACA,WAAA;EACA,YAAA;EACA,WAAA;EACA,SAAA;EACA,WAAA;EACA,uBAAA;EAEA,gBAAA;EACA,kBAAA;ACFF;;ADKA;EACE,yBAAA;ACFF;;ADKA;EAGE,2BAAA;ACFF;;ADKA;EACE;IACE,qCAAA;IACA,WAAA;IACA,SAAA;ECFF;EDIA;IACI,mDAAA;ECFJ;EDIA;IACI,mDAAA;ECFJ;EDIA;IACI,oBAAA;ECFJ;AACF;ADKA;EACE,SAAA;EACA,+BAAA;ACHF;;ADMA;EACE,uBAAA;EACA,eAAA;ACHF;;ADMA;EACE,kBAAA;EACA,aAAA;EACA,+CAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,YAAA;EACA,WAAA;ACHF","file":"content.css","sourcesContent":["@import url( \"./globals.css\" );\r\n\r\n.betterYT {\r\n font-size: 16px;\r\n font-weight: bold;\r\n text-align: center;\r\n}\r\n\r\n.betterYT-renderer {\r\n display: inline-block;\r\n}\r\n\r\n.betterYT-button-shape {\r\n display: flex;\r\n}\r\n\r\n.betterYT-volume-slider{\r\n -webkit-appearance: slider-vertical;\r\n position: absolute;\r\n right: -40px;\r\n top: 40px;\r\n opacity: 0.4;\r\n pointer-events: all !important;\r\n accent-color: var( --yt-brand-color );\r\n cursor: pointer;\r\n}\r\n\r\n@supports (-moz-appearance:none) { \r\n .volume-slider{\r\n right: 16px;\r\n }\r\n}\r\n\r\n.volume-slider:hover{\r\n opacity: 1;\r\n}\r\n\r\n.autoplay-switch {\r\n position: relative;\r\n display: block;\r\n width: 48px;\r\n height: 27px;\r\n margin: 0 auto;\r\n margin-bottom: -12px; // need to find a better way to do this\r\n}\r\n\r\n/* Hide default HTML checkbox */\r\n.autoplay-switch input {\r\n opacity: 0;\r\n width: 0;\r\n height: 0;\r\n}\r\n\r\n.autoplay-slider {\r\n position: absolute;\r\n cursor: pointer;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: #272727;\r\n -webkit-transition: .4s;\r\n transition: .4s;\r\n border-radius: 27px;\r\n\r\n}\r\n\r\n.autoplay-slider:hover {\r\n background-color: #3f3f3f;\r\n}\r\n\r\n.autoplay-slider:before {\r\n position: absolute;\r\n content: \"\";\r\n height: 20px;\r\n width: 20px;\r\n left: 3px;\r\n bottom: 3px;\r\n background-color: white;\r\n -webkit-transition: .4s;\r\n transition: .4s;\r\n border-radius: 50%;\r\n}\r\n\r\ninput:checked + .autoplay-slider {\r\n background-color: #3f3f3f;\r\n}\r\n\r\ninput:checked + .autoplay-slider:before {\r\n -webkit-transform: translateX(21px);\r\n -ms-transform: translateX(21px);\r\n transform: translateX(21px);\r\n}\r\n\r\n@media screen and (max-width: 599px) {\r\n .volumeSlider {\r\n -webkit-appearance: slider-horizontal;\r\n right: 44px;\r\n top: 18px;\r\n }\r\n .autoplay-slider {\r\n background-color: #FFFFFF1A;\r\n }\r\n input:checked + .autoplay-slider {\r\n background-color: #FFFFFF2A;\r\n }\r\n .betterYT-auto {\r\n padding-bottom: 16px;\r\n }\r\n}\r\n\r\n.betterYT-progress-bar{\r\n bottom: 0;\r\n pointer-events: auto !important;\r\n}\r\n\r\n.betterYT-progress-bar-hover{\r\n height: 10px !important;\r\n cursor: pointer;\r\n}\r\n\r\n.betterYT-timestamp-tooltip{\r\n position: absolute;\r\n display: none;\r\n background-color: #272727c4;\r\n border-radius: 5px;\r\n padding: 4px;\r\n font-size: 11px;\r\n color: white;\r\n z-index: 50;\r\n}","@import url(\"./globals.css\");\n.betterYT {\n font-size: 16px;\n font-weight: bold;\n text-align: center;\n}\n\n.betterYT-renderer {\n display: inline-block;\n}\n\n.betterYT-button-shape {\n display: flex;\n}\n\n.betterYT-volume-slider {\n -webkit-appearance: slider-vertical;\n position: absolute;\n right: -40px;\n top: 40px;\n opacity: 0.4;\n pointer-events: all !important;\n accent-color: var(--yt-brand-color);\n cursor: pointer;\n}\n\n@supports (-moz-appearance: none) {\n .volume-slider {\n right: 16px;\n }\n}\n.volume-slider:hover {\n opacity: 1;\n}\n\n.autoplay-switch {\n position: relative;\n display: block;\n width: 48px;\n height: 27px;\n margin: 0 auto;\n margin-bottom: -12px;\n}\n\n/* Hide default HTML checkbox */\n.autoplay-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.autoplay-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #272727;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: 27px;\n}\n\n.autoplay-slider:hover {\n background-color: #3f3f3f;\n}\n\n.autoplay-slider:before {\n position: absolute;\n content: \"\";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: 50%;\n}\n\ninput:checked + .autoplay-slider {\n background-color: #3f3f3f;\n}\n\ninput:checked + .autoplay-slider:before {\n -webkit-transform: translateX(21px);\n -ms-transform: translateX(21px);\n transform: translateX(21px);\n}\n\n@media screen and (max-width: 599px) {\n .volumeSlider {\n -webkit-appearance: slider-horizontal;\n right: 44px;\n top: 18px;\n }\n .autoplay-slider {\n background-color: rgba(255, 255, 255, 0.1019607843);\n }\n input:checked + .autoplay-slider {\n background-color: rgba(255, 255, 255, 0.1647058824);\n }\n .betterYT-auto {\n padding-bottom: 16px;\n }\n}\n.betterYT-progress-bar {\n bottom: 0;\n pointer-events: auto !important;\n}\n\n.betterYT-progress-bar-hover {\n height: 10px !important;\n cursor: pointer;\n}\n\n.betterYT-timestamp-tooltip {\n position: absolute;\n display: none;\n background-color: rgba(39, 39, 39, 0.768627451);\n border-radius: 5px;\n padding: 4px;\n font-size: 11px;\n color: white;\n z-index: 50;\n}"]} \ No newline at end of file diff --git a/src/css/content.scss b/src/css/content.scss new file mode 100644 index 0000000..f440ec5 --- /dev/null +++ b/src/css/content.scss @@ -0,0 +1,131 @@ +@import url( "./globals.css" ); + +.betterYT { + font-size: 16px; + font-weight: bold; + text-align: center; +} + +.betterYT-renderer { + display: inline-block; +} + +.betterYT-button-shape { + display: flex; +} + +.betterYT-volume-slider{ + -webkit-appearance: slider-vertical; + position: absolute; + right: -40px; + top: 40px; + opacity: 0.4; + pointer-events: all !important; + accent-color: var( --yt-brand-color ); + cursor: pointer; +} + +@supports (-moz-appearance:none) { + .volume-slider{ + right: 16px; + } +} + +.volume-slider:hover{ + opacity: 1; +} + +.autoplay-switch { + position: relative; + display: block; + width: 48px; + height: 27px; + margin: 0 auto; + margin-bottom: -12px; // need to find a better way to do this +} + +/* Hide default HTML checkbox */ +.autoplay-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.autoplay-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #272727; + -webkit-transition: .4s; + transition: .4s; + border-radius: 27px; + +} + +.autoplay-slider:hover { + background-color: #3f3f3f; +} + +.autoplay-slider:before { + position: absolute; + content: ""; + height: 20px; + width: 20px; + left: 3px; + bottom: 3px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + border-radius: 50%; +} + +input:checked + .autoplay-slider { + background-color: #3f3f3f; +} + +input:checked + .autoplay-slider:before { + -webkit-transform: translateX(21px); + -ms-transform: translateX(21px); + transform: translateX(21px); +} + +@media screen and (max-width: 599px) { + .volumeSlider { + -webkit-appearance: slider-horizontal; + right: 44px; + top: 18px; + } + .autoplay-slider { + background-color: #FFFFFF1A; + } + input:checked + .autoplay-slider { + background-color: #FFFFFF2A; + } + .betterYT-auto { + padding-bottom: 16px; + } +} + +.betterYT-progress-bar{ + bottom: 0; + pointer-events: auto !important; +} + +.betterYT-progress-bar-hover{ + height: 10px !important; + cursor: pointer; +} + +.betterYT-timestamp-tooltip{ + position: absolute; + display: none; + background-color: #272727c4; + border-radius: 5px; + padding: 4px; + font-size: 11px; + color: white; + z-index: 50; +} \ No newline at end of file diff --git a/src/css/globals.css b/src/css/globals.css new file mode 100644 index 0000000..d19921b --- /dev/null +++ b/src/css/globals.css @@ -0,0 +1,29 @@ +:root { + --text-primary-color: #030303; + --text-secondary-color: #888; + --bg-primary-color: #fff; + --bg-modal-color: #ffffff33; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #f2f2f2; + --bg-secondary-hover-color: #e6e6e6; + --separation-line-color: #e5e5e5; + --yt-brand-color: #f00; + --suggested-action-color: #378de9; + --call-to-action-color: #3ea6ff; + --max-height: 600px; +} + +@media (prefers-color-scheme: dark) { + :root { + --text-primary-color: #fff; + --text-secondary-color: #888; + --bg-primary-color: #0f0f0f; + --bg-modal-color: #0f0f0f66; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #272727; + --bg-secondary-hover-color: #3d3d3d; + --separation-line-color: #3f3f3f; + --suggested-action-color: #1d5fd4; + --call-to-action-color: #3978e6; + } +}/*# sourceMappingURL=globals.css.map */ \ No newline at end of file diff --git a/src/css/globals.css.map b/src/css/globals.css.map new file mode 100644 index 0000000..d375107 --- /dev/null +++ b/src/css/globals.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["globals.scss","globals.css"],"names":[],"mappings":"AAAA;EAEE,6BAAA;EACA,4BAAA;EACA,wBAAA;EACA,2BAAA;EACA,iCAAA;EACA,6BAAA;EACA,mCAAA;EACA,gCAAA;EACA,sBAAA;EACA,iCAAA;EACA,+BAAA;EAEA,mBAAA;ACDF;;ADIA;EAEE;IAEE,0BAAA;IACA,4BAAA;IACA,2BAAA;IACA,2BAAA;IACA,iCAAA;IACA,6BAAA;IACA,mCAAA;IACA,gCAAA;IACA,iCAAA;IACA,+BAAA;ECHF;AACF","file":"globals.css","sourcesContent":[":root\r\n{\r\n --text-primary-color: #030303;\r\n --text-secondary-color: #888;\r\n --bg-primary-color: #fff;\r\n --bg-modal-color: #ffffff33;\r\n --bg-primary-hover-color: #9d9ea1;\r\n --bg-secondary-color: #f2f2f2;\r\n --bg-secondary-hover-color: #e6e6e6;\r\n --separation-line-color: #e5e5e5;\r\n --yt-brand-color: #f00;\r\n --suggested-action-color: #378de9;\r\n --call-to-action-color: #3ea6ff;\r\n\r\n --max-height: 600px; // of the chrome popup \r\n}\r\n\r\n@media (prefers-color-scheme: dark) \r\n{\r\n :root\r\n {\r\n --text-primary-color: #fff;\r\n --text-secondary-color: #888;\r\n --bg-primary-color: #0f0f0f;\r\n --bg-modal-color: #0f0f0f66;\r\n --bg-primary-hover-color: #9d9ea1;\r\n --bg-secondary-color: #272727;\r\n --bg-secondary-hover-color: #3d3d3d;\r\n --separation-line-color: #3f3f3f;\r\n --suggested-action-color: #1d5fd4;\r\n --call-to-action-color: #3978e6;\r\n }\r\n}",":root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n --max-height: 600px;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}"]} \ No newline at end of file diff --git a/src/css/globals.scss b/src/css/globals.scss new file mode 100644 index 0000000..41f4df4 --- /dev/null +++ b/src/css/globals.scss @@ -0,0 +1,33 @@ +:root +{ + --text-primary-color: #030303; + --text-secondary-color: #888; + --bg-primary-color: #fff; + --bg-modal-color: #ffffff33; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #f2f2f2; + --bg-secondary-hover-color: #e6e6e6; + --separation-line-color: #e5e5e5; + --yt-brand-color: #f00; + --suggested-action-color: #378de9; + --call-to-action-color: #3ea6ff; + + --max-height: 600px; // of the chrome popup +} + +@media (prefers-color-scheme: dark) +{ + :root + { + --text-primary-color: #fff; + --text-secondary-color: #888; + --bg-primary-color: #0f0f0f; + --bg-modal-color: #0f0f0f66; + --bg-primary-hover-color: #9d9ea1; + --bg-secondary-color: #272727; + --bg-secondary-hover-color: #3d3d3d; + --separation-line-color: #3f3f3f; + --suggested-action-color: #1d5fd4; + --call-to-action-color: #3978e6; + } +} \ No newline at end of file diff --git a/src/css/style.css b/src/css/popup.css similarity index 92% rename from src/css/style.css rename to src/css/popup.css index 56f326c..0609363 100644 --- a/src/css/style.css +++ b/src/css/popup.css @@ -1,32 +1,4 @@ -:root { - --text-primary-color: #030303; - --text-secondary-color: #888; - --bg-primary-color: #fff; - --bg-modal-color: #ffffff33; - --bg-primary-hover-color: #9d9ea1; - --bg-secondary-color: #f2f2f2; - --bg-secondary-hover-color: #e6e6e6; - --separation-line-color: #e5e5e5; - --yt-brand-color: #f00; - --suggested-action-color: #378de9; - --call-to-action-color: #3ea6ff; - --max-height: 600px; -} - -@media (prefers-color-scheme: dark) { - :root { - --text-primary-color: #fff; - --text-secondary-color: #888; - --bg-primary-color: #0f0f0f; - --bg-modal-color: #0f0f0f66; - --bg-primary-hover-color: #9d9ea1; - --bg-secondary-color: #272727; - --bg-secondary-hover-color: #3d3d3d; - --separation-line-color: #3f3f3f; - --suggested-action-color: #1d5fd4; - --call-to-action-color: #3978e6; - } -} +@import url("./globals.css"); :where(h1, h2, h3, @@ -617,4 +589,4 @@ input:checked + .autoplay-slider:before { font-size: 11px; color: white; z-index: 50; -}/*# sourceMappingURL=style.css.map */ \ No newline at end of file +}/*# sourceMappingURL=popup.css.map */ \ No newline at end of file diff --git a/src/css/popup.css.map b/src/css/popup.css.map new file mode 100644 index 0000000..20bec74 --- /dev/null +++ b/src/css/popup.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["popup.scss","popup.css"],"names":[],"mappings":"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","file":"popup.css","sourcesContent":["\n@import url( \"./globals.css\" );\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\n*\n{\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n// Separator\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading\n{\n text-align: center;\n margin: 5px 0px\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms; \n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n\n.--page-indicator-container\n{\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n .--page-indicator,\n .--page-indicator-active\n {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n }\n \n // todo - change this to be an svg (fill + stroke)\n .--page-indicator\n {\n border: 2px solid var( --text-secondary-color );\n background: transparent;\n }\n .--page-indicator-active\n {\n border: 2px solid var( --text-primary-color );\n background: var( --text-primary-color );\n }\n}\n\n.--footer-button-container\n{\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n \n .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n\n background-color: transparent;\n color: var(--yt-brand-color );\n outline: 1px var( --yt-brand-color ) solid;\n\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n }\n \n .--footer-button:hover {\n background-color: var(--yt-brand-color );\n color: var(--text-primary-color);\n }\n}\n\n// this is for the github link\n.--global-footer\n{\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n\n .--global-footer-link\n {\n color: var(--text-secondary-color);\n }\n}\n\n.--modal\n{\n position: absolute;\n top: 0;\n left: 0;\n\n width: 100%;\n height: 100%;\n\n align-items: center;\n justify-content: center;\n \n background: var( --bg-modal-color );\n\n .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n \n .--modal-header-text\n {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n \n .--modal-command\n {\n font-style: normal;\n display: block;\n font-weight: bold;\n }\n \n }\n \n }\n\n .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n }\n\n .--modal-input {\n\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n\n transition: background 200ms;\n }\n .--modal-input:hover\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input:focus\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input-error,\n .--modal-input-success\n {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n }\n .--modal-input-error\n {\n color: red // ! - change to more accurate\n }\n .--modal-input-success\n {\n color: lime// ! - change to more accurate\n }\n\n .--modal-label\n {\n color: var(--text-secondary-color);\n font-style: italic;\n\n span\n {\n font-style: normal;\n font-weight: bold;\n }\n }\n \n}\n\n.key-combo-warning\n{\n color: var(--text-secondary-color);\n}\n\n.betterYT {\n font-size: 16px;\n font-weight: bold;\n text-align: center;\n}\n\n.betterYT-renderer {\n display: inline-block;\n}\n\n.betterYT-button-shape {\n display: flex;\n}\n\n.betterYT-volume-slider{\n -webkit-appearance: slider-vertical;\n position: absolute;\n right: -40px;\n top: 40px;\n opacity: 0.4;\n pointer-events: all !important;\n cursor: pointer;\n}\n\n@supports (-moz-appearance:none) { \n .volume-slider{\n right: 16px;\n }\n}\n\n.volume-slider:hover{\n opacity: 1;\n}\n\n.autoplay-switch {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 27px;\n}\n\n/* Hide default HTML checkbox */\n.autoplay-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.autoplay-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #272727;\n -webkit-transition: .4s;\n transition: .4s;\n border-radius: 27px;\n}\n\n.autoplay-slider:hover {\n background-color: #3f3f3f;\n}\n\n.autoplay-slider:before {\n position: absolute;\n content: \"\";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n -webkit-transition: .4s;\n transition: .4s;\n border-radius: 50%;\n}\n\ninput:checked + .autoplay-slider {\n background-color: #3f3f3f;\n}\n\ninput:checked + .autoplay-slider:before {\n -webkit-transform: translateX(21px);\n -ms-transform: translateX(21px);\n transform: translateX(21px);\n}\n\n@media screen and (max-width: 599px) {\n .volumeSlider {\n -webkit-appearance: slider-horizontal;\n right: 44px;\n top: 18px;\n }\n .autoplay-slider {\n background-color: #FFFFFF1A;\n }\n input:checked + .autoplay-slider {\n background-color: #FFFFFF2A;\n }\n .betterYT-auto {\n padding-bottom: 16px;\n }\n}\n\n.betterYT-progress-bar{\n bottom: 0;\n pointer-events: auto !important;\n}\n\n.betterYT-progress-bar-hover{\n height: 10px !important;\n cursor: pointer;\n}\n\n.betterYT-timestamp-tooltip{\n position: absolute;\n display: none;\n background-color: #272727c4;\n border-radius: 5px;\n padding: 4px;\n font-size: 11px;\n color: white;\n z-index: 50;\n}","@import url(\"./globals.css\");\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading {\n text-align: center;\n margin: 5px 0px;\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n.--page-indicator-container {\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n}\n.--page-indicator-container button, .--page-indicator-container input[type=submit], .--page-indicator-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--page-indicator-container .--page-indicator,\n.--page-indicator-container .--page-indicator-active {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n}\n.--page-indicator-container .--page-indicator {\n border: 2px solid var(--text-secondary-color);\n background: transparent;\n}\n.--page-indicator-container .--page-indicator-active {\n border: 2px solid var(--text-primary-color);\n background: var(--text-primary-color);\n}\n\n.--footer-button-container {\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n}\n.--footer-button-container button, .--footer-button-container input[type=submit], .--footer-button-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--footer-button-container .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: transparent;\n color: var(--yt-brand-color);\n outline: 1px var(--yt-brand-color) solid;\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n.--footer-button-container .--footer-button:hover {\n background-color: var(--yt-brand-color);\n color: var(--text-primary-color);\n}\n\n.--global-footer {\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n}\n.--global-footer .--global-footer-link {\n color: var(--text-secondary-color);\n}\n\n.--modal {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n align-items: center;\n justify-content: center;\n background: var(--bg-modal-color);\n}\n.--modal .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.--modal .--modal-header .--modal-header-text {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n}\n.--modal .--modal-header .--modal-header-text .--modal-command {\n font-style: normal;\n display: block;\n font-weight: bold;\n}\n.--modal .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n}\n.--modal .--modal-input {\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n transition: background 200ms;\n}\n.--modal .--modal-input:hover {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input:focus {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input-error,\n.--modal .--modal-input-success {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n}\n.--modal .--modal-input-error {\n color: red;\n}\n.--modal .--modal-input-success {\n color: lime;\n}\n.--modal .--modal-label {\n color: var(--text-secondary-color);\n font-style: italic;\n}\n.--modal .--modal-label span {\n font-style: normal;\n font-weight: bold;\n}\n\n.key-combo-warning {\n color: var(--text-secondary-color);\n}\n\n.betterYT {\n font-size: 16px;\n font-weight: bold;\n text-align: center;\n}\n\n.betterYT-renderer {\n display: inline-block;\n}\n\n.betterYT-button-shape {\n display: flex;\n}\n\n.betterYT-volume-slider {\n -webkit-appearance: slider-vertical;\n position: absolute;\n right: -40px;\n top: 40px;\n opacity: 0.4;\n pointer-events: all !important;\n cursor: pointer;\n}\n\n@supports (-moz-appearance: none) {\n .volume-slider {\n right: 16px;\n }\n}\n.volume-slider:hover {\n opacity: 1;\n}\n\n.autoplay-switch {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 27px;\n}\n\n/* Hide default HTML checkbox */\n.autoplay-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.autoplay-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #272727;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: 27px;\n}\n\n.autoplay-slider:hover {\n background-color: #3f3f3f;\n}\n\n.autoplay-slider:before {\n position: absolute;\n content: \"\";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: 50%;\n}\n\ninput:checked + .autoplay-slider {\n background-color: #3f3f3f;\n}\n\ninput:checked + .autoplay-slider:before {\n -webkit-transform: translateX(21px);\n -ms-transform: translateX(21px);\n transform: translateX(21px);\n}\n\n@media screen and (max-width: 599px) {\n .volumeSlider {\n -webkit-appearance: slider-horizontal;\n right: 44px;\n top: 18px;\n }\n .autoplay-slider {\n background-color: rgba(255, 255, 255, 0.1019607843);\n }\n input:checked + .autoplay-slider {\n background-color: rgba(255, 255, 255, 0.1647058824);\n }\n .betterYT-auto {\n padding-bottom: 16px;\n }\n}\n.betterYT-progress-bar {\n bottom: 0;\n pointer-events: auto !important;\n}\n\n.betterYT-progress-bar-hover {\n height: 10px !important;\n cursor: pointer;\n}\n\n.betterYT-timestamp-tooltip {\n position: absolute;\n display: none;\n background-color: rgba(39, 39, 39, 0.768627451);\n border-radius: 5px;\n padding: 4px;\n font-size: 11px;\n color: white;\n z-index: 50;\n}"]} \ No newline at end of file diff --git a/src/css/style.scss b/src/css/popup.scss similarity index 92% rename from src/css/style.scss rename to src/css/popup.scss index b699a57..70bd887 100644 --- a/src/css/style.scss +++ b/src/css/popup.scss @@ -1,36 +1,5 @@ -:root -{ - --text-primary-color: #030303; - --text-secondary-color: #888; - --bg-primary-color: #fff; - --bg-modal-color: #ffffff33; - --bg-primary-hover-color: #9d9ea1; - --bg-secondary-color: #f2f2f2; - --bg-secondary-hover-color: #e6e6e6; - --separation-line-color: #e5e5e5; - --yt-brand-color: #f00; - --suggested-action-color: #378de9; - --call-to-action-color: #3ea6ff; - - --max-height: 600px; // of the chrome popup -} - -@media (prefers-color-scheme: dark) -{ - :root - { - --text-primary-color: #fff; - --text-secondary-color: #888; - --bg-primary-color: #0f0f0f; - --bg-modal-color: #0f0f0f66; - --bg-primary-hover-color: #9d9ea1; - --bg-secondary-color: #272727; - --bg-secondary-hover-color: #3d3d3d; - --separation-line-color: #3f3f3f; - --suggested-action-color: #1d5fd4; - --call-to-action-color: #3978e6; - } -} + +@import url( "./globals.css" ); :where( h1, diff --git a/src/css/style.css.map b/src/css/style.css.map deleted file mode 100644 index d488dba..0000000 --- a/src/css/style.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["style.scss","style.css"],"names":[],"mappings":"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","file":"style.css","sourcesContent":[":root\n{\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n\n --max-height: 600px; // of the chrome popup \n}\n\n@media (prefers-color-scheme: dark) \n{\n :root\n {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n\n:where(\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n table,\n tr,\n td,\n th,\n p,\n li,\n span\n){\n color: var(--text-primary-color);\n}\n\n*\n{\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Source Sans Pro', 'Roboto', 'Noto', 'Arial', sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n// Separator\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading\n{\n text-align: center;\n margin: 5px 0px\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms; \n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px #00000020;\n}\n\n.extra_options--row\n{\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child()\n{\n margin-bottom: 0;\n}\n\n\n.extra_options--row input[type=\"number\"],\n.extra_options--row input[type=\"text\"]\n{\n outline: none;\n border: 1px var( --bg-secondary-hover-color ) solid;\n background: var( --bg-secondary-hover-color );\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n.extra_options--row input[type=\"number\"]:focus,\n.extra_options--row input[type=\"text\"]:focus\n{\n border: 2px var(--yt-brand-color) solid;\n}\n\n\n.extra_options--row input[type=\"checkbox\"],\n.extra_options--row input[type=\"radio\"],\n.extra_options--row input[type=\"range\"]\n{\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=\"number\"]::-webkit-outer-spin-button,\n.extra_options--row input[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=\"number\"] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n \n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n\n.--page-indicator-container\n{\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n\n .--page-indicator,\n .--page-indicator-active\n {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n }\n \n // todo - change this to be an svg (fill + stroke)\n .--page-indicator\n {\n border: 2px solid var( --text-secondary-color );\n background: transparent;\n }\n .--page-indicator-active\n {\n border: 2px solid var( --text-primary-color );\n background: var( --text-primary-color );\n }\n}\n\n.--footer-button-container\n{\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n\n button, input[type=\"submit\"], input[type=\"reset\"] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n }\n \n .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n\n background-color: transparent;\n color: var(--yt-brand-color );\n outline: 1px var( --yt-brand-color ) solid;\n\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n }\n \n .--footer-button:hover {\n background-color: var(--yt-brand-color );\n color: var(--text-primary-color);\n }\n}\n\n// this is for the github link\n.--global-footer\n{\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n\n .--global-footer-link\n {\n color: var(--text-secondary-color);\n }\n}\n\n.--modal\n{\n position: absolute;\n top: 0;\n left: 0;\n\n width: 100%;\n height: 100%;\n\n align-items: center;\n justify-content: center;\n \n background: var( --bg-modal-color );\n\n .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n \n .--modal-header-text\n {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n \n .--modal-command\n {\n font-style: normal;\n display: block;\n font-weight: bold;\n }\n \n }\n \n }\n\n .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n }\n\n .--modal-input {\n\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n\n transition: background 200ms;\n }\n .--modal-input:hover\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input:focus\n {\n background-color: var(--bg-primary-hover-color);\n }\n .--modal-input-error,\n .--modal-input-success\n {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n }\n .--modal-input-error\n {\n color: red // ! - change to more accurate\n }\n .--modal-input-success\n {\n color: lime// ! - change to more accurate\n }\n\n .--modal-label\n {\n color: var(--text-secondary-color);\n font-style: italic;\n\n span\n {\n font-style: normal;\n font-weight: bold;\n }\n }\n \n}\n\n.key-combo-warning\n{\n color: var(--text-secondary-color);\n}\n\n.betterYT {\n font-size: 16px;\n font-weight: bold;\n text-align: center;\n}\n\n.betterYT-renderer {\n display: inline-block;\n}\n\n.betterYT-button-shape {\n display: flex;\n}\n\n.betterYT-volume-slider{\n -webkit-appearance: slider-vertical;\n position: absolute;\n right: -40px;\n top: 40px;\n opacity: 0.4;\n pointer-events: all !important;\n cursor: pointer;\n}\n\n@supports (-moz-appearance:none) { \n .volume-slider{\n right: 16px;\n }\n}\n\n.volume-slider:hover{\n opacity: 1;\n}\n\n.autoplay-switch {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 27px;\n}\n\n/* Hide default HTML checkbox */\n.autoplay-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.autoplay-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #272727;\n -webkit-transition: .4s;\n transition: .4s;\n border-radius: 27px;\n}\n\n.autoplay-slider:hover {\n background-color: #3f3f3f;\n}\n\n.autoplay-slider:before {\n position: absolute;\n content: \"\";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n -webkit-transition: .4s;\n transition: .4s;\n border-radius: 50%;\n}\n\ninput:checked + .autoplay-slider {\n background-color: #3f3f3f;\n}\n\ninput:checked + .autoplay-slider:before {\n -webkit-transform: translateX(21px);\n -ms-transform: translateX(21px);\n transform: translateX(21px);\n}\n\n@media screen and (max-width: 599px) {\n .volumeSlider {\n -webkit-appearance: slider-horizontal;\n right: 44px;\n top: 18px;\n }\n .autoplay-slider {\n background-color: #FFFFFF1A;\n }\n input:checked + .autoplay-slider {\n background-color: #FFFFFF2A;\n }\n .betterYT-auto {\n padding-bottom: 16px;\n }\n}\n\n.betterYT-progress-bar{\n bottom: 0;\n pointer-events: auto !important;\n}\n\n.betterYT-progress-bar-hover{\n height: 10px !important;\n cursor: pointer;\n}\n\n.betterYT-timestamp-tooltip{\n position: absolute;\n display: none;\n background-color: #272727c4;\n border-radius: 5px;\n padding: 4px;\n font-size: 11px;\n color: white;\n z-index: 50;\n}",":root {\n --text-primary-color: #030303;\n --text-secondary-color: #888;\n --bg-primary-color: #fff;\n --bg-modal-color: #ffffff33;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #f2f2f2;\n --bg-secondary-hover-color: #e6e6e6;\n --separation-line-color: #e5e5e5;\n --yt-brand-color: #f00;\n --suggested-action-color: #378de9;\n --call-to-action-color: #3ea6ff;\n --max-height: 600px;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --text-primary-color: #fff;\n --text-secondary-color: #888;\n --bg-primary-color: #0f0f0f;\n --bg-modal-color: #0f0f0f66;\n --bg-primary-hover-color: #9d9ea1;\n --bg-secondary-color: #272727;\n --bg-secondary-hover-color: #3d3d3d;\n --separation-line-color: #3f3f3f;\n --suggested-action-color: #1d5fd4;\n --call-to-action-color: #3978e6;\n }\n}\n:where(h1,\nh2,\nh3,\nh4,\nh5,\nh6,\ntable,\ntr,\ntd,\nth,\np,\nli,\nspan) {\n color: var(--text-primary-color);\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: \"Source Sans Pro\", \"Roboto\", \"Noto\", \"Arial\", sans-serif;\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem 1rem;\n}\n\ntr:not(:first-child) {\n background-color: var(--bg-secondary-color);\n transition: all 0.3s ease;\n}\n\ntr:not(:first-child):hover {\n background-color: var(--bg-secondary-hover-color);\n}\n\n.separation-line {\n background-color: var(--separation-line-color);\n margin-top: 5px;\n opacity: 0.9;\n border: none;\n outline: none;\n}\n\n.popup_subheading {\n text-align: center;\n margin: 5px 0px;\n}\n\nsvg {\n fill: var(--bg-primary-hover-color);\n transition: all 0.5s ease;\n}\n\nsvg:hover {\n cursor: pointer;\n fill: var(--yt-brand-color) !important;\n}\n\n.container {\n width: 280px;\n}\n\n.title-container {\n display: flex;\n justify-content: space-between;\n flex-direction: row;\n}\n\n.title {\n font-size: 16px;\n font-weight: bold;\n}\n\n.version {\n color: var(--bg-primary-hover-color);\n}\n\n.separation-line {\n height: 1px;\n width: 100%;\n}\n\n.textbox {\n width: 100%;\n font-size: 12px;\n margin: 0;\n}\n\nlabel {\n font-size: 12px;\n}\n\n#extra_options_skip_threshold {\n margin-left: 4px;\n}\n\n.textbox:focus {\n outline: 0;\n border-color: var(--call-to-action-color);\n}\n\n.keybind-wrapper {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 10px;\n}\n\n.keybind-span {\n padding: 3px 7px;\n background-color: var(--bg-secondary-hover-color);\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: default;\n}\n\n.edit-svg {\n fill: var(--bg-primary-hover-color);\n}\n\n.edit-btn {\n background: none;\n outline: none;\n border: none;\n}\n\ntable {\n border-collapse: separate;\n border-spacing: 0 2px;\n}\n\nth {\n font-size: 12px;\n text-align: center;\n}\n\ntd {\n font-size: 12px;\n padding: 7px 5px;\n}\n\ntd:first-child {\n padding-left: 6px;\n}\n\ntd:first-child,\nth:first-child {\n border-bottom-left-radius: 5px;\n border-top-left-radius: 5px;\n cursor: default;\n}\n\ntd:last-child,\nth:last-child {\n border-bottom-right-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.footer {\n font-size: 10px;\n text-align: center;\n margin: 5px 0px;\n}\n\na {\n color: var(--text-primary-color);\n text-decoration: none;\n display: flex;\n}\n\n.btn-wrapper {\n display: flex;\n justify-content: center;\n flex-direction: row;\n gap: 8px;\n}\n\n.modal {\n display: none;\n position: fixed;\n z-index: 999;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n background-color: rgba(0, 0, 0, 0.4);\n}\n\n.modal-content {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n margin: auto;\n padding: 0 8px 8px 8px;\n width: 80%;\n border-radius: 5px;\n box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.7);\n}\n\n.close-btn {\n color: var(--bg-primary-hover-color);\n font-size: 20px;\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: color 200ms;\n}\n\n.close-btn:hover,\n.close-btn:focus {\n color: var(--yt-brand-color);\n text-decoration: none;\n cursor: pointer;\n}\n\n.input-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: center;\n margin: 10px 0px;\n gap: 10px;\n}\n\n.prevent-selection {\n -webkit-user-select: none; /* Safari */\n -ms-user-select: none; /* IE 10 and IE 11 */\n user-select: none; /* Standard syntax */\n}\n\n#keybind-input {\n width: 30%;\n height: 1.5rem;\n text-align: center;\n}\n\n#keybind-input:focus {\n outline: 0;\n outline: none !important;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.1254901961);\n}\n\n.extra_options--row {\n display: grid;\n grid-template-columns: 5fr 1fr;\n text-wrap: balance;\n margin-bottom: 16px;\n}\n\n.extra_options--row:last-child() {\n margin-bottom: 0;\n}\n\n.extra_options--row input[type=number],\n.extra_options--row input[type=text] {\n outline: none;\n border: 1px var(--bg-secondary-hover-color) solid;\n background: var(--bg-secondary-hover-color);\n color: var(--text-primary-color);\n padding: 0 1rem;\n border-radius: 100vh;\n width: 5rem;\n}\n\n.extra_options--row input[type=number]:focus,\n.extra_options--row input[type=text]:focus {\n border: 2px var(--yt-brand-color) solid;\n}\n\n.extra_options--row input[type=checkbox],\n.extra_options--row input[type=radio],\n.extra_options--row input[type=range] {\n accent-color: var(--yt-brand-color);\n}\n\n.extra_options--row input[type=number]::-webkit-outer-spin-button,\n.extra_options--row input[type=number]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n/* Firefox */\n.extra_options--row input[type=number] {\n -moz-appearance: textfield;\n}\n\n/* width */\n::-webkit-scrollbar {\n width: 0.5rem;\n}\n\n/* Track */\n::-webkit-scrollbar-track {\n background: transparent;\n height: 4rem;\n}\n\n/* Handle */\n::-webkit-scrollbar-thumb {\n background: var(--bg-secondary-color);\n height: 4rem;\n border-radius: 1rem;\n}\n\n/* Handle on hover */\n::-webkit-scrollbar-thumb:hover {\n background: var(--bg-secondary-hover-color);\n}\n\n.--page-indicator-container {\n display: flex;\n gap: 1rem;\n width: fit-content;\n margin: 0 auto;\n}\n.--page-indicator-container button, .--page-indicator-container input[type=submit], .--page-indicator-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--page-indicator-container .--page-indicator,\n.--page-indicator-container .--page-indicator-active {\n height: 1rem;\n aspect-ratio: 1/1;\n border-radius: 5rem;\n cursor: pointer;\n}\n.--page-indicator-container .--page-indicator {\n border: 2px solid var(--text-secondary-color);\n background: transparent;\n}\n.--page-indicator-container .--page-indicator-active {\n border: 2px solid var(--text-primary-color);\n background: var(--text-primary-color);\n}\n\n.--footer-button-container {\n display: flex;\n width: fit-content;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n max-width: 100%;\n margin: 0 auto;\n}\n.--footer-button-container button, .--footer-button-container input[type=submit], .--footer-button-container input[type=reset] {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n cursor: pointer;\n outline: inherit;\n}\n.--footer-button-container .--footer-button {\n margin: 5px 0px;\n padding: 5px 10px;\n background-color: transparent;\n color: var(--yt-brand-color);\n outline: 1px var(--yt-brand-color) solid;\n border-radius: 3px;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.275);\n cursor: pointer;\n transition: all 0.3s ease;\n font-size: 12px;\n}\n.--footer-button-container .--footer-button:hover {\n background-color: var(--yt-brand-color);\n color: var(--text-primary-color);\n}\n\n.--global-footer {\n display: flex;\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n}\n.--global-footer .--global-footer-link {\n color: var(--text-secondary-color);\n}\n\n.--modal {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n align-items: center;\n justify-content: center;\n background: var(--bg-modal-color);\n}\n.--modal .--modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.--modal .--modal-header .--modal-header-text {\n font-style: italic;\n color: var(--text-secondary-color);\n display: block;\n}\n.--modal .--modal-header .--modal-header-text .--modal-command {\n font-style: normal;\n display: block;\n font-weight: bold;\n}\n.--modal .--modal-content {\n background: var(--bg-primary-color);\n color: var(--text-primary-color);\n padding: 0.5rem;\n border-radius: 0.5rem;\n width: 80%;\n box-shadow: 0 0 0.5rem 0.5rem var(--bg-modal-color);\n}\n.--modal .--modal-input {\n outline: none;\n border: none;\n border-radius: 1rem;\n font-size: 1.5rem;\n width: 1rem;\n height: 1rem;\n padding: 1rem 2rem;\n background-color: var(--bg-secondary-color);\n caret-color: var(--text-primary-color);\n color: var(--text-primary-color);\n transition: background 200ms;\n}\n.--modal .--modal-input:hover {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input:focus {\n background-color: var(--bg-primary-hover-color);\n}\n.--modal .--modal-input-error,\n.--modal .--modal-input-success {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 0.25rem;\n}\n.--modal .--modal-input-error {\n color: red;\n}\n.--modal .--modal-input-success {\n color: lime;\n}\n.--modal .--modal-label {\n color: var(--text-secondary-color);\n font-style: italic;\n}\n.--modal .--modal-label span {\n font-style: normal;\n font-weight: bold;\n}\n\n.key-combo-warning {\n color: var(--text-secondary-color);\n}\n\n.betterYT {\n font-size: 16px;\n font-weight: bold;\n text-align: center;\n}\n\n.betterYT-renderer {\n display: inline-block;\n}\n\n.betterYT-button-shape {\n display: flex;\n}\n\n.betterYT-volume-slider {\n -webkit-appearance: slider-vertical;\n position: absolute;\n right: -40px;\n top: 40px;\n opacity: 0.4;\n pointer-events: all !important;\n cursor: pointer;\n}\n\n@supports (-moz-appearance: none) {\n .volume-slider {\n right: 16px;\n }\n}\n.volume-slider:hover {\n opacity: 1;\n}\n\n.autoplay-switch {\n position: relative;\n display: inline-block;\n width: 48px;\n height: 27px;\n}\n\n/* Hide default HTML checkbox */\n.autoplay-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.autoplay-slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #272727;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: 27px;\n}\n\n.autoplay-slider:hover {\n background-color: #3f3f3f;\n}\n\n.autoplay-slider:before {\n position: absolute;\n content: \"\";\n height: 20px;\n width: 20px;\n left: 3px;\n bottom: 3px;\n background-color: white;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: 50%;\n}\n\ninput:checked + .autoplay-slider {\n background-color: #3f3f3f;\n}\n\ninput:checked + .autoplay-slider:before {\n -webkit-transform: translateX(21px);\n -ms-transform: translateX(21px);\n transform: translateX(21px);\n}\n\n@media screen and (max-width: 599px) {\n .volumeSlider {\n -webkit-appearance: slider-horizontal;\n right: 44px;\n top: 18px;\n }\n .autoplay-slider {\n background-color: rgba(255, 255, 255, 0.1019607843);\n }\n input:checked + .autoplay-slider {\n background-color: rgba(255, 255, 255, 0.1647058824);\n }\n .betterYT-auto {\n padding-bottom: 16px;\n }\n}\n.betterYT-progress-bar {\n bottom: 0;\n pointer-events: auto !important;\n}\n\n.betterYT-progress-bar-hover {\n height: 10px !important;\n cursor: pointer;\n}\n\n.betterYT-timestamp-tooltip {\n position: absolute;\n display: none;\n background-color: rgba(39, 39, 39, 0.768627451);\n border-radius: 5px;\n padding: 4px;\n font-size: 11px;\n color: white;\n z-index: 50;\n}"]} \ No newline at end of file diff --git a/src/lib/ActionElement.ts b/src/lib/ActionElement.ts new file mode 100644 index 0000000..8eb95a4 --- /dev/null +++ b/src/lib/ActionElement.ts @@ -0,0 +1,143 @@ +import { setPlaybackRate } from "./PlaybackRate" +import { getActionElement, getCurrentId, getVideo } from "./getters" +import { render, wheel } from "./utils" + +export function populateActionElement( state: any, settings: any ) // ! use proper types +{ + const id = getCurrentId() + const actionElement = getActionElement() + const ytShorts = getVideo() + + if ( !actionElement ) return + if ( !ytShorts ) return + + const betterYTContainer = document.createElement("div") + betterYTContainer.id = "betterYT-container" + betterYTContainer.setAttribute("class", "button-container style-scope ytd-reel-player-overlay-renderer") + + const ytdButtonRenderer = document.createElement("div") + ytdButtonRenderer.setAttribute("class", "betterYT-renderer style-scope ytd-reel-player-overlay-renderer") + + const ytButtonShape = document.createElement("div") + ytButtonShape.setAttribute("class", "betterYT-button-shape") + + const ytLabel = document.createElement("label") + ytLabel.setAttribute("class", "yt-spec-button-shape-with-label") + + const ytButton = document.createElement("button") + ytButton.setAttribute("class", "yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-l yt-spec-button-shape-next--icon-button ") + // Playback Rate + var para0 = document.createElement("p") + para0.classList.add("betterYT") + para0.id = `ytPlayback${id}` + + + // Timer + const ytTimer = document.createElement("div") + ytTimer.classList.add("yt-spec-button-shape-with-label__label") + var span1 = document.createElement("span") + span1.setAttribute("class", "yt-core-attributed-string yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--text-alignment-center yt-core-attributed-string--word-wrapping") + span1.id = `ytTimer${getCurrentId()}` + span1.setAttribute("role", "text") + ytTimer.appendChild(span1) + + + + // Match YT's HTML structure + ytButton.appendChild(para0) + ytLabel.appendChild(ytButton) + ytLabel.appendChild(ytTimer) + ytButtonShape.appendChild(ytLabel) + ytdButtonRenderer.appendChild(ytButtonShape) + betterYTContainer.appendChild(ytdButtonRenderer) + + + actionElement.insertBefore(betterYTContainer, actionElement.children[1]) + + // Autoplay Switch + const autoplaySwitch = ` +
+ + + Autoplay +
+ ` + + actionElement.insertBefore( render( autoplaySwitch ), actionElement.children[1] ) + + // const autoplayTitle = ` + //
+ // Autoplay + //
+ // ` + + // actionElement.insertBefore( render( autoplayTitle ), actionElement.children[2] ) + + + ytShorts.playbackRate = state.playbackRate + setPlaybackRate( state ) + // injectedSuccess = setTimer( currTime || 0, Math.round(ytShorts.duration || 0)) + + betterYTContainer.addEventListener("click", () => { + ytShorts.playbackRate = 1 + state.playbackRate = ytShorts.playbackRate + }) + + document.getElementById( `autoplay-checkbox${getCurrentId()}` )?.addEventListener('change', ( e: any ) => { + if ( e.target.checked ) + { + localStorage.setItem("yt-autoplay", "true") + ytShorts.loop = false + } + else + { + localStorage.setItem("yt-autoplay", "false") + ytShorts.loop = true + } + }) + + + wheel( + ytButton, + () => { + // speedup + const video = getVideo() + if ( video === null ) return + + if (video.playbackRate < 16) video.playbackRate += 0.25 + state.playbackRate = video.playbackRate + + }, + () => { + // speeddown + const video = getVideo() + if ( video === null ) return + + if (video.playbackRate > 0.25) video.playbackRate -= 0.25 + state.playbackRate = video.playbackRate + } + ) + + wheel( + ytTimer, + () => { + // forward + const video = getVideo() + if ( video !== null ) video.currentTime += 1 + }, + () => { + // backward + const video = getVideo() + if ( video !== null ) video.currentTime -= 1 + } + ) +} \ No newline at end of file diff --git a/src/lib/Autoplay.ts b/src/lib/Autoplay.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/ProgressBar.ts b/src/lib/ProgressBar.ts new file mode 100644 index 0000000..7cc31bc --- /dev/null +++ b/src/lib/ProgressBar.ts @@ -0,0 +1,97 @@ +import { getOverlayElement, getVideo } from "./getters" +import { render } from "./utils" + +export function modifyProgressBar() +{ + const overlayElement = getOverlayElement() + const ytShorts = getVideo() + + if ( !overlayElement ) return + if ( ytShorts === null ) return + + //[id="0"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #overlay + let progressBar = overlayElement.children[3].children[0].children[0] as HTMLElement // ? the progressbar itself + let pbBackground = progressBar.children[0] as HTMLElement // ? the grey background of the bar + let pbForeground = progressBar.children[1] as HTMLElement // ? The red part of the progress bar + + const subBox = overlayElement.children[1] as HTMLElement + const tooltip = render( `
` ) as HTMLElement + + progressBar.removeAttribute( "hidden" ) // ? to show on shorter shorts + progressBar.appendChild( tooltip ); + + // Styling to ensure rest of bottom overlay (shorts title/sub button) stay in place + subBox.style.marginBottom = "-7px" // ? changed to 1 from 0 bc i think its not targeting the right thing + progressBar.style.height = "10px" + progressBar.style.paddingTop = "2px" // Slight padding to increase hover box + + progressBar.classList.add('betterYT-progress-bar') + pbBackground.classList.add('betterYT-progress-bar') + pbForeground.classList.add('betterYT-progress-bar') + + addListeners({ + progressBar, + pbBackground, + pbForeground, + tooltip + }) +} + +interface ListenerProps { + progressBar: HTMLElement, + pbBackground: HTMLElement, + pbForeground: HTMLElement, + tooltip: HTMLElement, +} +function addListeners( { progressBar, pbBackground, pbForeground, tooltip }: ListenerProps ) +{ + const ytShorts = getVideo() + if ( ytShorts === null ) return + + + progressBar.addEventListener("mouseover", () => { + pbBackground.classList.add('betterYT-progress-bar-hover') + pbForeground.classList.add('betterYT-progress-bar-hover') + }) + progressBar.addEventListener("mousemove", ( e: MouseEvent ) => { + + let x = e.clientX - ytShorts.getBoundingClientRect().left + // Deal with slight inaccuracies + if (x < 0) x = 0 + if (x > ytShorts.clientWidth) x = ytShorts.clientWidth + // Get timestamp and round to nearest 0.1 + let timestamp = ( (x / ytShorts.clientWidth) * ytShorts.duration ).toFixed(1) + tooltip.textContent = `${timestamp}s` + + // Ensure tooltip stays visible at edges of client + if ((x - (tooltip.offsetWidth / 2)) > (ytShorts.clientWidth - tooltip.offsetWidth)) + { + tooltip.style.left = `${ytShorts.clientWidth - tooltip.offsetWidth}px` + } + else if ((x - (tooltip.offsetWidth / 2)) <= 0) + { + tooltip.style.left = "0px" + } + else + { + tooltip.style.left = `${x - (tooltip.offsetWidth / 2)}px` + } + + tooltip.style.top = "-20px" + tooltip.style.display = 'block' + }) + + + progressBar.addEventListener("mouseout", () => { + pbBackground.classList.remove('betterYT-progress-bar-hover') + pbForeground.classList.remove('betterYT-progress-bar-hover') + tooltip.style.display = 'none' + }) + + progressBar.addEventListener("click", (event) => { + let x = (event).clientX - ytShorts.getBoundingClientRect().left + if (x < 0) x = 0 + if (x > ytShorts.clientWidth) x = ytShorts.clientWidth + ytShorts.currentTime = (x / ytShorts.clientWidth) * ytShorts.duration + }) +} \ No newline at end of file diff --git a/src/lib/VolumeSlider.ts b/src/lib/VolumeSlider.ts index e85b6f2..628191d 100644 --- a/src/lib/VolumeSlider.ts +++ b/src/lib/VolumeSlider.ts @@ -2,6 +2,7 @@ import { saveSettingsToStorage } from "./SaveToStorage" import { storage } from "./declarations" import { StateObject } from "./definitions" import { getCurrentId, getVideo, getVolumeContainer, getVolumeSliderController } from "./getters" +import { render } from "./utils" export function checkVolume( settings: any ) { @@ -34,31 +35,41 @@ export function setVolume( settings: any, newVolume: number ) } -// todo - add proper type export function setVolumeSlider( state: StateObject, settings: any ) { - const ytShorts = getVideo() - const id = state.id + const id = getCurrentId() const volumeContainer = getVolumeContainer() - const slider = document.createElement("input") + // const slider = document.createElement("input") + const slider = render(` + + `) - if( state.userVolume === null ) state.userVolume = 0.5 + if( state.volume === null ) state.volume = 0.5 // checkVolume(ytShorts) // todo - uncomment this when added - slider.id = `volumeSliderController${id}` - slider.classList.add("volume-slider") - slider.classList.add("betterYT-volume-slider") - slider.type = "range" - slider.min = "0" - slider.max = "1" - slider.step = "0.01" - slider.setAttribute("orient", "vertical") - volumeContainer.appendChild(slider) - slider.value = state.userVolume - - slider.addEventListener( "input", (e: any) => setVolume( settings, e.target.valueAsNumber ) ) + // slider.id = `volumeSliderController${id}` + // slider.classList.add("volume-slider") + // slider.classList.add("betterYT-volume-slider") + // slider.type = "range" + // slider.min = "0" + // slider.max = "1" + // slider.step = "0.01" + // slider.setAttribute("orient", "vertical") + // slider.value = state.userVolume + volumeContainer.appendChild( slider ) + // Prevent video from pausing/playing on click - slider.addEventListener("click", data => data.stopPropagation() ) + slider.addEventListener( "input", (e: any) => setVolume( settings, e.target.valueAsNumber ) ) + slider.addEventListener( "click", e => e.stopPropagation() ) } \ No newline at end of file diff --git a/src/lib/declarations.ts b/src/lib/declarations.ts index 55ff905..ea1be5e 100644 --- a/src/lib/declarations.ts +++ b/src/lib/declarations.ts @@ -78,8 +78,11 @@ export const storage = BROWSER.storage.local export const DEFAULT_STATE = { id : 0, topId : 0, - volumeState : 0, + volume : 0, playbackRate: 1, + lastTime : -1, // ? this is for checking if items were injected + + injectedItems: new Set(), actualVolume: null, skippedId : null, diff --git a/src/lib/getters.ts b/src/lib/getters.ts index 9118b07..05be0fd 100644 --- a/src/lib/getters.ts +++ b/src/lib/getters.ts @@ -45,16 +45,19 @@ export const getCommentCount = ( id: number | null ) => { return !isNaN(commentCount as number) ? commentCount : 0 } -export const getActionElement = ( id: number | null ) => +export const getActionElement = () => document.querySelector( - `[id="${id}"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #actions` + `[id="${getCurrentId()}"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #actions` ) as HTMLElement -export const getOverlayElement = ( id: number | null ) => - document.querySelector( - `[id="${id}"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #overlay` +export function getOverlayElement() +{ + return document.querySelector( + `[id="${ getCurrentId() }"] > div.overlay.style-scope.ytd-reel-video-renderer > ytd-reel-player-overlay-renderer > #overlay` ) as HTMLElement +} + export function getVolumeContainer() { const id = getCurrentId() @@ -83,4 +86,9 @@ export function getVolumeSliderController() { const id = getCurrentId() return document.getElementById(`volumeSliderController${id}`) +} + +export function getProgressBarList() +{ + return getOverlayElement().children[3].children[0].children[0] } \ No newline at end of file diff --git a/src/lib/skipShort.ts b/src/lib/skipShort.ts index 46502fd..0a6f765 100644 --- a/src/lib/skipShort.ts +++ b/src/lib/skipShort.ts @@ -33,13 +33,7 @@ export function shouldSkipShort( state: any, options: any, currentId: string, li return true } -/** - * If the setting `shouldSkipUnrecommendedShorts` is true, skip shorts that have fewer than the set number of likes - */ - - // fixed mac scroll issue -export function skipShort( short: HTMLVideoElement ) +export function skipShort() { - var nextButton = getNextButton(); - nextButton.click(); + getNextButton()?.click() } From f7b1f9a450cafbd5d214b633d24e7a07ce18be65 Mon Sep 17 00:00:00 2001 From: adsuth Date: Fri, 11 Aug 2023 01:43:39 +0100 Subject: [PATCH 07/60] completion of alpha (for now) --- ROADMAP.md | 34 +++-- package-lock.json | 11 +- package.json | 3 +- src/components/EditButton.tsx | 29 +--- src/components/EditModal.tsx | 89 +++++++++--- src/components/FeaturesPage.tsx | 97 +++++++++++++ src/components/KeybindsPage.tsx | 8 +- src/components/OptionsPage.tsx | 15 ++- src/components/PageIndicator.tsx | 50 ++++++- src/components/PageIndicatorContainer.tsx | 9 +- src/components/Popup.tsx | 43 +++--- src/content.ts | 108 ++++----------- src/css/popup.css | 124 +++++++++++------ src/css/popup.css.map | 2 +- src/css/popup.scss | 157 ++++++++++++++++------ src/lib/ActionElement.ts | 96 +++++-------- src/lib/AutomaticallyOpenComments.ts | 38 ++++++ src/lib/Autoplay.ts | 50 +++++++ src/lib/InjectionSuccess.ts | 51 +++++++ src/lib/PlaybackRate.ts | 21 ++- src/lib/ProgressBar.ts | 16 ++- src/lib/ResetDefaults.ts | 38 +++++- src/lib/SaveToStorage.ts | 8 ++ src/lib/SkipShortsWithLowLikes.ts | 48 +++++++ src/lib/VideoState.ts | 37 +++++ src/lib/VolumeSlider.ts | 35 ++--- src/lib/changeShort.ts | 8 -- src/lib/chromeEmitters.ts | 6 +- src/lib/declarations.ts | 57 +++++++- src/lib/definitions.ts | 19 ++- src/lib/getters.ts | 32 ++++- src/lib/handleKeyEvent.ts | 61 +++++---- src/lib/retrieveFromStorage.ts | 29 +++- src/lib/skipShort.ts | 39 ------ 34 files changed, 1034 insertions(+), 434 deletions(-) create mode 100644 src/components/FeaturesPage.tsx create mode 100644 src/lib/AutomaticallyOpenComments.ts create mode 100644 src/lib/InjectionSuccess.ts create mode 100644 src/lib/SkipShortsWithLowLikes.ts create mode 100644 src/lib/VideoState.ts delete mode 100644 src/lib/changeShort.ts delete mode 100644 src/lib/skipShort.ts diff --git a/ROADMAP.md b/ROADMAP.md index 6456c31..f691d71 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,13 +7,22 @@ ## Current Changes and Improvements from v2 - Options and Keybinds now take effect immediately -- Added "tabbed page" view to split up keybinds, options, and anything else in the future +- Added "tabbed" page view for the popup to split up keybinds, options, and anything else in the future +- Fixed issue where volume would ignore slider sporadically +- Added "disable key bind" option in the edit keybind modal +- You can now reset options to defaults (options = extraoptions) +- Added "settings" object to storage, to contain excess content-script settings like volume and autoplay +- Added "features" object to storage, you can now enable and disable certain content script UI elements (autoplay, volume, etc) + + +## Codebase Format - the `lib` directory contains exports for both the content and popup scripts. I recommend containing each bit of unrelated functionality in their own files. - This excludes the `declarations.ts`, which should contain global variables used throughout the program for easy access - All global types (including interfaces and enums) are defined in `definitions.ts` - `utils.ts` is a file for utility functions that are generic. Basically think anything that could be transferred to a different project - Finally, theres `getters.ts` which is for selector functions (eg *getVideo()*) - the `components` directory is for react components. Try splitting up your TSX into reusable components if possible (its not too big of a deal if you cant mind you) +- Using an npm module for the icons (react-icons). Ideally, they should be imported from there (also stick to the material design ones for consistency) --- ## General @@ -22,17 +31,24 @@ ## Content Script - ~~Separate Popup and Content CSS into their own files (prevent weird side effects)~~ - ~~Implement the seek bar~~ -- Implement the volume slider - - Save setting to storage +- ~~Implement the volume slider~~ + - ~~Save setting to storage~~ + - Slider should automatically toggle youtube's built in mute button when on 0 or >0 respectively (just do a synthetic click on the btn) - Clean up code; move each element to their own script - ~~⚠️ **error from recent chrome update may be unfixed**~~ +- Fix styling for autoplay (when comments are open, doesnt follow the transparent effect of other buttons) ## Popup -- Add missing functionality from more recent main branch patches (copy from the main branch): - - Option to **auto open comments**, and appropriate logic - - Option to **change the seek amount** -- Add proper icons for the tabs see [this icon pack](https://fonts.google.com/icons) -- Tweak styling for the indicators (padding and margins look off) +- ~~Add missing functionality from more recent main branch patches (copy from the main branch):~~ + - ~~Option to **auto open comments**, and appropriate logic~~ + - ~~Option to **change the seek amount**~~ +- ~~Add proper icons for the tabs see [this icon pack](https://fonts.google.com/icons)~~ +- ~~Tweak styling for the indicators (padding and margins look off)~~ - ⚠️ **Update logo when a new logo is decided if needed** - Remove console logs **that aren't prefaced with "[BYS] :: "** -- ~~Fix issue with number and text inputs on the option page losing focus on input (on change changes the state, perhaps we need to instead update on loss of focus, not change)~~ \ No newline at end of file +- ~~Fix issue with number and text inputs on the option page losing focus on input (on change changes the state, perhaps we need to instead update on loss of focus, not change)~~ + +## Language Support +Not a priority of mine, but I do have an idea of how to accomodate other languages, but its a bit finnicky. +It'd also require help from native localisers ideally. +Essentially, we could have an object of "terms" (could this be JSON?), with the key being the language code from `navigator.language` diff --git a/package-lock.json b/package-lock.json index 529ceca..e34d2a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "react-icons": "^4.10.1" }, "devDependencies": { "@crxjs/vite-plugin": "^1.0.14", @@ -1840,6 +1841,14 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-refresh": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.13.0.tgz", diff --git a/package.json b/package.json index 4364957..8b169b3 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ }, "dependencies": { "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "react-icons": "^4.10.1" }, "devDependencies": { "@crxjs/vite-plugin": "^1.0.14", diff --git a/src/components/EditButton.tsx b/src/components/EditButton.tsx index c32dbf5..f8eae38 100644 --- a/src/components/EditButton.tsx +++ b/src/components/EditButton.tsx @@ -2,17 +2,7 @@ import React from 'react' import { setKeybind, storage } from '../lib/declarations' import { StringDictionary } from '../lib/definitions' -const edit_button_svg = (<> - - - - - - - - - -) +import { MdCreate } from "react-icons/md" interface Props { keybindsState: StringDictionary, @@ -23,30 +13,15 @@ interface Props { } export default function EditButton( { keybindsState, setKeybindsState, command, setSelectedCommand, setIsModalOpen }: Props ) { - // todo - add on click event + props - function handleEditButtonClick( command: string ): void { - console.log( `Clicked: ${command}` ) - setIsModalOpen( true ) setSelectedCommand( command ) - - // setKeybindState( () => { - // const newState = setKeybind( keybindState, command, key ) - - // storage.set( { "extraopts" : newState } ) - // localStorage.setItem( "yt-extraopts", JSON.stringify( newState ) ) - - // console.log( `[BYS] :: Set keybind "${command}" to ${key}` ) - - // return newState - // } ) } return ( ) } diff --git a/src/components/EditModal.tsx b/src/components/EditModal.tsx index eb39b1e..2c87ed9 100644 --- a/src/components/EditModal.tsx +++ b/src/components/EditModal.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react' import Separator from './Separator' -import { DEFAULT_PRESSED_KEY, EXCLUDED_KEY_BINDS } from '../lib/declarations' +import { DEFAULT_PRESSED_KEY, DISABLED_BIND_STRING, EXCLUDED_KEY_BINDS } from '../lib/declarations' import { StringDictionary } from '../lib/definitions' import { saveKeybindsToStorage } from '../lib/SaveToStorage' @@ -32,12 +32,17 @@ export default function EditModal( { selectedCommand, isModalOpen, setIsModalOpe }, [ isModalOpen ] ) useEffect( () => { + if ( + pressedKey === DEFAULT_PRESSED_KEY || + pressedKey === DISABLED_BIND_STRING + ) return + setInputErrorString( null ) setInputSuccessString( null ) + + if ( canUseKey() ) + setInputSuccessString( `"${pressedKey}" can be used!` ) - if ( pressedKey === DEFAULT_PRESSED_KEY ) return - if ( !canUseKey() ) setInputErrorString( `"${pressedKey}" cannot be used ` ) - else setInputSuccessString( `"${pressedKey}" can be used! Close to confirm.` ) }, [ pressedKey ] ) let currentKey = "" @@ -45,6 +50,15 @@ export default function EditModal( { selectedCommand, isModalOpen, setIsModalOpe currentKey = keybindsState[ selectedCommand ] function handleCloseModal() + { + setIsModalOpen( false ) + + setInputErrorString( null ) + setInputSuccessString( null ) + setPressedKey( DEFAULT_PRESSED_KEY ) + } + + function handleSaveBind() { if ( canUseKey() ) { @@ -66,19 +80,20 @@ export default function EditModal( { selectedCommand, isModalOpen, setIsModalOpe setPressedKey( DEFAULT_PRESSED_KEY ) } - function handleModalInput( e: any ) // todo - change to be actual type - { - const code = e.code - setPressedKey( code ) - } - function canUseKey() { - return ( - !EXCLUDED_KEY_BINDS.includes( pressedKey ) && - !Object.values( keybindsState as Object ).includes( pressedKey ) && - pressedKey !== DEFAULT_PRESSED_KEY - ) + if ( EXCLUDED_KEY_BINDS.includes( pressedKey ) ) + { + setInputErrorString( `"${pressedKey}" cannot be used ` ) + return false + } + if ( Object.values( keybindsState as Object ).includes( pressedKey ) ) + { + setInputErrorString( `"${pressedKey}" is already in use` ) + return false + } + + return true } function showInputInfoString() @@ -90,6 +105,40 @@ export default function EditModal( { selectedCommand, isModalOpen, setIsModalOpe return
{"\u00A0"}
} + function handleDisableBind() + { + setKeybindsState( () => { + const newState = {...keybindsState} + newState[ selectedCommand ] = DISABLED_BIND_STRING + + saveKeybindsToStorage( newState ) + console.log( `[BYS] :: Disabled binding "${pressedKey}" that was bound to ${selectedCommand}` ) + + setInputSuccessString( `"${selectedCommand}" was disabled!` ) + return newState + } ) + + setPressedKey( DISABLED_BIND_STRING ) + + } + + function getCurrentKeybindString() + { + if ( currentKey === DISABLED_BIND_STRING ) + return <>Keybind is currently disabled + + return <>Current bind is {currentKey} + } + + function showConfirmButton() + { + if ( !inputSuccessString ) return <> + + return ( + + ) + } + return (
- + {pressedKey} {showInputInfoString()} +
+ + {showConfirmButton()} +
Does not support key combinations
diff --git a/src/components/FeaturesPage.tsx b/src/components/FeaturesPage.tsx new file mode 100644 index 0000000..9faee0c --- /dev/null +++ b/src/components/FeaturesPage.tsx @@ -0,0 +1,97 @@ +import { DEFAULT_FEATURES, FEATURES_ORDER, setFeature } from '../lib/declarations' +import { determineInputType } from '../lib/utils' +import { PolyDictionary } from '../lib/definitions' +import { disableAllFeatures, enableAllFeatures } from '../lib/ResetDefaults' +import { saveFeaturesToStorage } from '../lib/SaveToStorage' +import { useEffect, useState } from 'react' + +interface Props +{ + featuresState: PolyDictionary + setFeaturesState: ( features: ( previousState: PolyDictionary ) => PolyDictionary ) => void +} + +export default function FeaturesPage( { featuresState, setFeaturesState }: Props ) { + // this only exists to rerender on change + + const [ buttonEnablesAll, setButtonEnablesAll ] = useState(true) // ! - change + + useEffect( () => { + setButtonEnablesAll( () => { + if ( featuresState === null ) return true + const featuresStateLength = Object.keys( featuresState ).length + return Object.values( featuresState ).filter( ( feature: boolean ) => feature ).length !== featuresStateLength + } ) + }, [ featuresState ] ) + + function handleResetFeaturesClick() + { + setFeaturesState( () => { + let newState + + if ( buttonEnablesAll ) newState = enableAllFeatures() + else newState = disableAllFeatures() + + return newState + } ) + } + + function handleFeatureChange( e: any, feature: string ) + { + const value = e.target.checked + + setFeaturesState( () => { + const newState = setFeature( featuresState, feature, value ) + + saveFeaturesToStorage( newState ) + console.log( `[BYS] :: Set Feature "${feature}" to ${value}` ) + + return newState + } ) + } + + function populateFeaturesPage() + { + if ( featuresState === null ) return <> + + return FEATURES_ORDER.map( ( feature: string, i: number ) => { + const value = featuresState[ feature ] + + if ( featuresState === null ) return + + return ( +
+ + handleFeatureChange( e, feature ) } + checked = { value } + /> +
+ ) + } ) + } + + return ( + <> +

Toggle Features

+ +
+ { populateFeaturesPage() } +
+ +

Changes will only affect new Shorts

+ +
+ + + Github + +
+ + ) +} diff --git a/src/components/KeybindsPage.tsx b/src/components/KeybindsPage.tsx index e34b360..f5d7050 100644 --- a/src/components/KeybindsPage.tsx +++ b/src/components/KeybindsPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import { DEFAULT_KEYBINDS, KEYBINDS_ORDER } from '../lib/declarations' +import { DEFAULT_KEYBINDS, DISABLED_BIND_STRING, KEYBINDS_ORDER } from '../lib/declarations' import EditButton from './EditButton' import { resetKeybinds } from '../lib/ResetDefaults' import { StringDictionary } from '../lib/definitions' @@ -31,7 +31,7 @@ export default function KeybindsPage( { setKeybindsState, keybindsState }: Props return DEFAULT_KEYBINDS } ) } - + function populateKeybindsPage() { if ( keybindsState === null ) return <> @@ -76,8 +76,8 @@ export default function KeybindsPage( { setKeybindsState, keybindsState }: Props -