Merge tag '4.5.0' into stable
This commit is contained in:
commit
2f802f4e68
14
CHANGELOG.md
14
CHANGELOG.md
@ -13,12 +13,22 @@ QoL improvements for me:
|
||||
|
||||
* logging: allow to enable logging at will and export said logs to a file
|
||||
|
||||
### v4.5.0
|
||||
|
||||
* Under the hood: migrated from vue2 to vue3, because optional chaining in templates is too OP.
|
||||
* (On options page, section 'Action & shortcuts') Manual aspect ratio now supports entering custom ratios using '21/9' and '2.39:1' formats (as opposed to single number, e.g. '2.39') — [#121](https://github.com/tamius-han/ultrawidify/issues/121).
|
||||
* Added config for wakanim.tv (special thanks to @saschanaz for doing the legwork — [#113](https://github.com/tamius-han/ultrawidify/issues/113))
|
||||
* (In Firefox) When extension was placed in overflow menu, the popup was cut off. That should be fixed now. [#119](https://github.com/tamius-han/ultrawidify/issues/119)
|
||||
* The extension will now show a notification when autodetection can't run due to DRM
|
||||
* Videos on facebook and reddit no longer get shifted up and to the left for me (cropping most of the video off-screen), but I haven't been deliberately trying to fix that issue. If you experience that issue, please consider contacting me (via github or email) with a link to a problematic video.
|
||||
|
||||
### v4.4.10 (Current)
|
||||
|
||||
* Video alignment should now work on Twitch [#109](https://github.com/tamius-han/ultrawidify/issues/109)
|
||||
* Videos should now align properly on Hulu while cropped [#111](https://github.com/tamius-han/ultrawidify/issues/111) & via email
|
||||
* Video alignment should now work on Twitch — [#109](https://github.com/tamius-han/ultrawidify/issues/109)
|
||||
* Videos should now align properly on Hulu while cropped — [#111](https://github.com/tamius-han/ultrawidify/issues/111) & via email
|
||||
* Fixed a problem where changing certain settings would cause multiple instances of Ultrawidify to run on a page, effectively preventing some crop options to be set until reload. (possibly [#112](https://github.com/tamius-han/ultrawidify/issues/112)?)
|
||||
* Fixed a problem where embedded videos would be misaligned after switching from full screen
|
||||
* **[4.4.10.1]** Fixed cruncyhroll regression — [#109](https://github.com/tamius-han/ultrawidify/issues/115)
|
||||
|
||||
### v4.4.9
|
||||
|
||||
|
9032
package-lock.json
generated
9032
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
42
package.json
42
package.json
@ -1,39 +1,44 @@
|
||||
{
|
||||
"name": "ultrawidify",
|
||||
"version": "4.4.9",
|
||||
"version": "4.5.0",
|
||||
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
|
||||
"author": "Tamius Han <tamius.han@gmail.com>",
|
||||
"scripts": {
|
||||
"start": "npm run dev",
|
||||
"pre-build": "rm -rf ./dist-ff; rm -rf ./dist-chrome; rm -rf ./node_modules; npm ci",
|
||||
"build": "npm run pre-build; cross-env NODE_ENV=production BROWSER=firefox CHANNEL=stable webpack --hide-modules",
|
||||
"build-chrome": "cross-env NODE_ENV=production BROWSER=chrome CHANNEL=stable webpack --hide-modules",
|
||||
"build-edge": "cross-env NODE_ENV=production BROWSER=edge CHANNEL=stable webpack --hide-modules",
|
||||
"build:dev": "webpack --hide-modules",
|
||||
"build-testing": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=testing webpack --hide-modules",
|
||||
"build-nightly": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=nightly webpack --hide-modules",
|
||||
"build-testing-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=testing webpack --hide-modules",
|
||||
"build-nightly-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=nightly webpack --hide-modules",
|
||||
"build-chrome:dev": "cross-env NODE_ENV=development BROWSER=chrome webpack --hide-modules",
|
||||
"build-all": "mkdir -p ./build/old; npm run pre-build; rm ./dist-zip/uw-amo-source.zip; mv -f ./dist-zip/*.zip ./build/old; npm run build; node scripts/build-zip.js ff; npm run build-chrome; node scripts/build-zip.js chrome; ./scripts/prepare-amo-source.sh",
|
||||
"build-chrome": "cross-env NODE_ENV=production BROWSER=chrome CHANNEL=stable webpack --hide-modules",
|
||||
"build-chrome:dev": "cross-env NODE_ENV=development BROWSER=chrome webpack --hide-modules",
|
||||
"build-edge": "cross-env NODE_ENV=production BROWSER=edge CHANNEL=stable webpack --hide-modules",
|
||||
"build-nightly": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=nightly webpack --hide-modules",
|
||||
"build-nightly-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=nightly webpack --hide-modules",
|
||||
"build-testing": "cross-env NODE_ENV=development BROWSER=firefox CHANNEL=testing webpack --hide-modules",
|
||||
"build-testing-chrome": "cross-env NODE_ENV=development BROWSER=chrome CHANNEL=testing webpack --hide-modules",
|
||||
"build-zip": "node scripts/build-zip.js",
|
||||
"dev": "cross-env NODE_ENV=development CHANNEL=dev concurrently \"cross-env BROWSER=firefox npm run build:dev -- --watch\" \"cross-env BROWSER=chrome npm run build:dev -- --watch\""
|
||||
"build:dev": "webpack --hide-modules",
|
||||
"dev": "cross-env NODE_ENV=development CHANNEL=dev concurrently \"cross-env BROWSER=firefox npm run build:dev -- --watch\" \"cross-env BROWSER=chrome npm run build:dev -- --watch\"",
|
||||
"pre-build": "rm -rf ./dist-ff; rm -rf ./dist-chrome; rm -rf ./node_modules; npm ci",
|
||||
"start": "npm run dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/core-js": "^2.5.3",
|
||||
"@types/es6-promise": "^3.3.0",
|
||||
"@vue/cli": "^4.5.9",
|
||||
"bootstrap": "^4.5.3",
|
||||
"bootstrap-icons": "^1.1.0",
|
||||
"bootstrap-icons-vue": "^0.3.0",
|
||||
"bootstrap-vue": "^2.20.1",
|
||||
"concurrently": "^5.2.0",
|
||||
"fs-extra": "^7.0.1",
|
||||
"json-cyclic": "0.0.3",
|
||||
"vue": "^2.6.11",
|
||||
"vuex": "^3.5.1",
|
||||
"vuex-webextensions": "^1.3.0",
|
||||
"webextension-polyfill": "^0.6.0"
|
||||
"vue": "^3.0.0-beta.1",
|
||||
"vuex": "^4.0.0-alpha.1",
|
||||
"vuex-webextensions": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.10.4",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@vue/compiler-sfc": "^3.0.3",
|
||||
"archiver": "^3.0.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"copy-webpack-plugin": "^4.5.3",
|
||||
@ -44,9 +49,10 @@
|
||||
"mini-css-extract-plugin": "^0.4.4",
|
||||
"node-sass": "^4.14.1",
|
||||
"sass-loader": "^7.1.0",
|
||||
"vue-loader": "^15.9.3",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vue-cli-plugin-vue-next": "~0.1.4",
|
||||
"vue-loader": "^16.0.0",
|
||||
"web-ext-types": "^2.1.0",
|
||||
"webextension-polyfill": "^0.6.0",
|
||||
"webpack": "^4.44.0",
|
||||
"webpack-chrome-extension-reloader": "^0.8.3",
|
||||
"webpack-cli": "^3.3.12",
|
||||
|
@ -151,98 +151,99 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import '../../res/css/colors.scss';
|
||||
|
||||
.action {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.uw-ultrawidify-container-root {
|
||||
.action {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.action .command-details {
|
||||
height: 0px;
|
||||
max-height: 0px;
|
||||
transition: max-height 0.5s ease;
|
||||
overflow: hidden;
|
||||
transition: height 0.5s ease;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
}
|
||||
.action .command-details {
|
||||
height: 0px;
|
||||
max-height: 0px;
|
||||
transition: max-height 0.5s ease;
|
||||
overflow: hidden;
|
||||
transition: height 0.5s ease;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.action:hover .command-details {
|
||||
height: auto;
|
||||
max-height: 200px;
|
||||
transition: max-height 0.5s ease;
|
||||
}
|
||||
.action:hover .command-details {
|
||||
height: auto;
|
||||
max-height: 200px;
|
||||
transition: max-height 0.5s ease;
|
||||
}
|
||||
|
||||
.command-details {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
}
|
||||
.command-details {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.action-name-cmd-container, .p1rem {
|
||||
padding: 1rem;
|
||||
}
|
||||
.action-name-cmd-container, .p1rem {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.cd-pad {
|
||||
padding: 0.5em;
|
||||
}
|
||||
.cd-pad {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.action-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 300;
|
||||
color: $text-normal;
|
||||
width: 50%;
|
||||
}
|
||||
.action-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 300;
|
||||
color: $text-normal;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.action-name:hover, .action:hover .action-name {
|
||||
color: lighten($primary-color, 20%);
|
||||
}
|
||||
.action-name:hover, .action:hover .action-name {
|
||||
color: lighten($primary-color, 20%);
|
||||
}
|
||||
|
||||
.red {
|
||||
color: $primary-color !important;
|
||||
}
|
||||
.red {
|
||||
color: $primary-color !important;
|
||||
}
|
||||
|
||||
.cmd-container {
|
||||
width: 13.37rem;
|
||||
}
|
||||
.cmd-container {
|
||||
width: 13.37rem;
|
||||
}
|
||||
|
||||
.cmdlist {
|
||||
font-family: 'Overpass Mono';
|
||||
font-size: 0.9rem;
|
||||
color: $text-dim;
|
||||
}
|
||||
.cmdlist {
|
||||
font-family: 'Overpass Mono';
|
||||
font-size: 0.9rem;
|
||||
color: $text-dim;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: 600;
|
||||
}
|
||||
.bold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.scope-scope {
|
||||
width: 5rem;
|
||||
text-align: right !important;
|
||||
color: $secondary-color;
|
||||
}
|
||||
.scope-scope {
|
||||
width: 5rem;
|
||||
text-align: right !important;
|
||||
color: $secondary-color;
|
||||
}
|
||||
|
||||
.scope-visible {
|
||||
width: 7rem;
|
||||
}
|
||||
.scope-visible {
|
||||
width: 7rem;
|
||||
}
|
||||
|
||||
.scope-button-label {
|
||||
width: 16rem;
|
||||
}
|
||||
.scope-button-label {
|
||||
width: 16rem;
|
||||
}
|
||||
|
||||
.scope-row-label {
|
||||
color: $text-dark;
|
||||
}
|
||||
.scope-row-label {
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
.scope-row-highlight {
|
||||
color: $text-normal;
|
||||
}
|
||||
.scope-row-highlight {
|
||||
color: $text-normal;
|
||||
}
|
||||
|
||||
.transparent {
|
||||
opacity: 0;
|
||||
.transparent {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
96
src/common/components/Icon.vue
Normal file
96
src/common/components/Icon.vue
Normal file
File diff suppressed because one or more lines are too long
@ -1,23 +1,25 @@
|
||||
.json-level-indent {
|
||||
padding-left: 2em !important;
|
||||
font-family: 'Overpass Mono', monospace;
|
||||
}
|
||||
.item-key {
|
||||
color: rgb(255, 196, 148);
|
||||
}
|
||||
.item-key-boolean-false {
|
||||
color: rgb(207, 149, 101)
|
||||
}
|
||||
.uw-ultrawidify-container-root {
|
||||
.json-level-indent {
|
||||
padding-left: 2em !important;
|
||||
font-family: 'Overpass Mono', monospace;
|
||||
}
|
||||
.item-key {
|
||||
color: rgb(255, 196, 148);
|
||||
}
|
||||
.item-key-boolean-false {
|
||||
color: rgb(207, 149, 101)
|
||||
}
|
||||
|
||||
.json-value-boolean-true {
|
||||
color: rgb(150, 240, 198);
|
||||
}
|
||||
.json-value-boolean-false {
|
||||
color: rgb(241, 21, 21);
|
||||
}
|
||||
.json-value-number {
|
||||
color: rgb(121, 121, 238);
|
||||
}
|
||||
.json-value-string {
|
||||
color: rgb(226, 175, 7);
|
||||
.json-value-boolean-true {
|
||||
color: rgb(150, 240, 198);
|
||||
}
|
||||
.json-value-boolean-false {
|
||||
color: rgb(241, 21, 21);
|
||||
}
|
||||
.json-value-number {
|
||||
color: rgb(121, 121, 238);
|
||||
}
|
||||
.json-value-string {
|
||||
color: rgb(226, 175, 7);
|
||||
}
|
||||
}
|
||||
|
@ -21,33 +21,35 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.mt2px {
|
||||
margin-top: 7px;
|
||||
}
|
||||
.id {
|
||||
border: 1px solid #d00;
|
||||
background-color: rgba(216, 94, 24, 0.25)
|
||||
}
|
||||
.class {
|
||||
border: 1px solid #33d;
|
||||
background-color: rgba(69, 69, 255, 0.25)
|
||||
}
|
||||
.closeButton {
|
||||
margin-top: 2px;
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
font-size: 1.5em;
|
||||
color: #e00;
|
||||
}
|
||||
.closeButton:hover {
|
||||
cursor: pointer;
|
||||
color: #fa6;
|
||||
}
|
||||
.qsb {
|
||||
cursor:default;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
margin-bottom: 2px;
|
||||
<style lang="scss" scoped>
|
||||
.uw-ultrawidify-container-root {
|
||||
.mt2px {
|
||||
margin-top: 7px;
|
||||
}
|
||||
.id {
|
||||
border: 1px solid #d00;
|
||||
background-color: rgba(216, 94, 24, 0.25)
|
||||
}
|
||||
.class {
|
||||
border: 1px solid #33d;
|
||||
background-color: rgba(69, 69, 255, 0.25)
|
||||
}
|
||||
.closeButton {
|
||||
margin-top: 2px;
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
font-size: 1.5em;
|
||||
color: #e00;
|
||||
}
|
||||
.closeButton:hover {
|
||||
cursor: pointer;
|
||||
color: #fa6;
|
||||
}
|
||||
.qsb {
|
||||
cursor:default;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -20,11 +20,13 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.center-text {
|
||||
text-align: center;
|
||||
}
|
||||
.dark {
|
||||
opacity: 50%;
|
||||
<style lang="scss" scoped>
|
||||
.uw-ultrawidify-container-root {
|
||||
.center-text {
|
||||
text-align: center;
|
||||
}
|
||||
.dark {
|
||||
opacity: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
14
src/common/data/notifications.js
Normal file
14
src/common/data/notifications.js
Normal file
@ -0,0 +1,14 @@
|
||||
let Notifications = Object.freeze({
|
||||
'TEST_NOTIFICATION': {
|
||||
icon: 'card-text',
|
||||
text: 'This is a test notification.',
|
||||
timeout: -1,
|
||||
},
|
||||
'AARD_DRM': {
|
||||
icon: 'exclamation-triangle',
|
||||
text: '<b>Autodetection cannot run on this video.</b> This usually happens when sites use DRM. You will have to set aspect ratio manually.',
|
||||
timeout: 5000,
|
||||
}
|
||||
});
|
||||
|
||||
export default Notifications;
|
@ -1,5 +1,5 @@
|
||||
if (process.env.CHANNEL !== 'stable') {
|
||||
console.log('Loaded ExtensionMode');
|
||||
console.info('Loading ExtensionMode');
|
||||
}
|
||||
|
||||
|
||||
@ -12,4 +12,8 @@ var ExtensionMode = Object.freeze({
|
||||
Enabled: 3,
|
||||
});
|
||||
|
||||
if (process.env.CHANNEL !== 'stable') {
|
||||
console.info('Loaded ExtensionMode');
|
||||
}
|
||||
|
||||
export default ExtensionMode;
|
||||
|
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
<div class="flex flex-row flex-end w100">
|
||||
<div v-if="!showTextMode" class="button" @click="showTextMode = true">
|
||||
Paste config ...
|
||||
<Icon icon="clipboard-plus" style="font-size: 2em"></Icon> Paste config ...
|
||||
</div>
|
||||
<div v-else class="button" @click="showTextMode = false">
|
||||
Back
|
||||
@ -164,10 +164,12 @@ import Logger from '../ext/lib/Logger';
|
||||
import Comms from '../ext/lib/comms/Comms';
|
||||
import IO from '../common/js/IO';
|
||||
import JsonObject from '../common/components/JsonEditor/JsonObject';
|
||||
import Icon from '../common/components/Icon';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
JsonObject,
|
||||
Icon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -304,134 +306,134 @@ export default {
|
||||
@import '../res/css/font/overpass-mono.css';
|
||||
@import '../res/css/common.scss';
|
||||
|
||||
.uw-ultrawidify-container-root {
|
||||
.root-window {
|
||||
position: fixed !important;
|
||||
top: 5vh !important;
|
||||
left: 5vw !important;
|
||||
width: 90vw !important;
|
||||
height: 90vh !important;
|
||||
z-index: 999999 !important;
|
||||
background-color: rgba( $page-background, 0.9) !important;
|
||||
color: #f1f1f1 !important;
|
||||
font-size: 14px !important;
|
||||
|
||||
.root-window {
|
||||
position: fixed !important;
|
||||
top: 5vh !important;
|
||||
left: 5vw !important;
|
||||
width: 90vw !important;
|
||||
height: 90vh !important;
|
||||
z-index: 999999 !important;
|
||||
background-color: rgba( $page-background, 0.9) !important;
|
||||
color: #f1f1f1 !important;
|
||||
font-size: 14px !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
div {
|
||||
font-family: 'Overpass';
|
||||
}
|
||||
|
||||
div {
|
||||
font-family: 'Overpass';
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
font-family: 'Overpass Thin';
|
||||
}
|
||||
h1 {
|
||||
font-size: 4em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.header {
|
||||
h1, h2 {
|
||||
font-family: 'Overpass Thin';
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: -0.20em;
|
||||
margin-top: 0.0em;
|
||||
font-size: 4em;
|
||||
}
|
||||
.header-top, .header-bottom {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
}
|
||||
.header-top {
|
||||
background-color: $popup-header-background !important;
|
||||
|
||||
.header {
|
||||
h1 {
|
||||
margin-bottom: -0.20em;
|
||||
margin-top: 0.0em;
|
||||
}
|
||||
.header-top, .header-bottom {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
.header-top {
|
||||
background-color: $popup-header-background !important;
|
||||
}
|
||||
.header-bottom {
|
||||
font-size: 1.75em;
|
||||
}
|
||||
}
|
||||
.header-bottom {
|
||||
font-size: 1.75em;
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
padding: 8px 32px;
|
||||
width: 100%;
|
||||
}
|
||||
.settings-panel {
|
||||
box-sizing: border-box;
|
||||
padding-right: 8px;
|
||||
flex-grow: 2 !important;
|
||||
min-width: 30% !important;
|
||||
flex-shrink: 0 !important;
|
||||
height: inherit !important;
|
||||
}
|
||||
.results-panel {
|
||||
box-sizing: border-box;
|
||||
padding-left: 8px;
|
||||
max-width: 70% !important;
|
||||
flex-grow: 5 !important;
|
||||
flex-shrink: 0 !important;
|
||||
height: inherit !important;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: 'Overpass Mono';
|
||||
}
|
||||
|
||||
.m-025em {
|
||||
margin: 0.25em;
|
||||
}
|
||||
|
||||
.p-t-025em {
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background-color: $primary;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button-big {
|
||||
font-size: 1.5em;
|
||||
padding: 1.75em 3.25em;
|
||||
}
|
||||
|
||||
.button-bar {
|
||||
font-size: 1.25em;
|
||||
padding: 0.25em 1.25em;
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
|
||||
.button-header {
|
||||
font-size: 2em;
|
||||
padding-top: 0.1em;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.jsonbg {
|
||||
background-color: #131313;
|
||||
}
|
||||
.jsonbg-error {
|
||||
background-color: #884420;
|
||||
}
|
||||
|
||||
.log-config-margin {
|
||||
margin-top: 3em;
|
||||
margin-bottom: 3em;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
padding: 8px 32px;
|
||||
width: 100%;
|
||||
}
|
||||
.settings-panel {
|
||||
box-sizing: border-box;
|
||||
padding-right: 8px;
|
||||
flex-grow: 2 !important;
|
||||
min-width: 30% !important;
|
||||
flex-shrink: 0 !important;
|
||||
height: inherit !important;
|
||||
}
|
||||
.results-panel {
|
||||
box-sizing: border-box;
|
||||
padding-left: 8px;
|
||||
max-width: 70% !important;
|
||||
flex-grow: 5 !important;
|
||||
flex-shrink: 0 !important;
|
||||
height: inherit !important;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: 'Overpass Mono';
|
||||
}
|
||||
|
||||
.m-025em {
|
||||
margin: 0.25em;
|
||||
}
|
||||
|
||||
.p-t-025em {
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background-color: $primary;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button-big {
|
||||
font-size: 1.5em;
|
||||
padding: 1.75em 3.25em;
|
||||
}
|
||||
|
||||
.button-bar {
|
||||
font-size: 1.25em;
|
||||
padding: 0.25em 1.25em;
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
|
||||
.button-header {
|
||||
font-size: 2em;
|
||||
padding-top: 0.1em;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.jsonbg {
|
||||
background-color: #131313;
|
||||
}
|
||||
.jsonbg-error {
|
||||
background-color: #884420;
|
||||
}
|
||||
|
||||
.log-config-margin {
|
||||
margin-top: 3em;
|
||||
margin-bottom: 3em;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
216
src/csui/NotificationUi.vue
Normal file
216
src/csui/NotificationUi.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div v-if="showNotification" class="uw-ultrawidify-container flex flex-column overflow-hidden">
|
||||
<div class="notification-popup flex flex-row">
|
||||
<div v-if="notificationIcon" class="flex-nogrow flex-noshrink notification-icon">
|
||||
<Icon
|
||||
class="flex-nogrow flex-noshrink"
|
||||
:icon="notificationIcon"
|
||||
>
|
||||
</Icon>
|
||||
</div>
|
||||
<div class="notification-content flex-grow flex-shrink flex flex-column flex-cross-center">
|
||||
<div
|
||||
class="notification-text"
|
||||
v-html="notificationText"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-if="notificationActions"
|
||||
class="action-buttons flex flex-row"
|
||||
>
|
||||
<div
|
||||
v-for="action of notificationActions"
|
||||
class="action-button"
|
||||
:key="action"
|
||||
@click="action.command"
|
||||
>
|
||||
<Icon v-if="action.icon" :icon="action.icon"></Icon>{{action.label}}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="hideActions"
|
||||
class="hide-actions"
|
||||
>
|
||||
Never show again:<wbr>
|
||||
<template
|
||||
v-for="action of hideActions"
|
||||
:key="action"
|
||||
>
|
||||
<i @click="closeNotification">
|
||||
<a
|
||||
class="hide-action-button"
|
||||
@click="action.command"
|
||||
>
|
||||
{{action.label}}
|
||||
</a>
|
||||
<wbr>
|
||||
</i>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="notification-icon action-button"
|
||||
@click="closeNotification()"
|
||||
>
|
||||
<Icon
|
||||
class="flex-nogrow flex-noshrink"
|
||||
icon="x"
|
||||
>
|
||||
</Icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import Icon from '../common/components/Icon';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notificationTimeout: null,
|
||||
notificationIcon: "exclamation-triangle",
|
||||
notificationText: "<b>Sample text.</b> This will be replaced with real notification later.",
|
||||
notificationActions: null,
|
||||
hideActions: null,
|
||||
showNotification: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'notificationConfig'
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
/**
|
||||
* Sets new notification config. Currently, we can only show one notification at a time.
|
||||
*
|
||||
* We expect a config object like this:
|
||||
* {
|
||||
* timeout: number — how long we'll be displaying the notification. If empty, 10s. -1: until user dismisses it
|
||||
* icon: string — what icon we're gonna show. We're using bootstrap icons. Can be empty.
|
||||
* text: — notification text. Supports HTML.
|
||||
* notificationActions: [
|
||||
* {
|
||||
* command: function that gets executed upon clicking the button.
|
||||
* label: label of the button
|
||||
* icon: icon of the button
|
||||
* }
|
||||
* ],
|
||||
* hideOptions: [
|
||||
* // more of notificationActions except it's a special case
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
notificationConfig(newConfig) {
|
||||
if (newConfig) {
|
||||
this.notificationText = newConfig.text;
|
||||
this.notificationActions = newConfig.notificationActions;
|
||||
this.notificationIcon = newConfig.icon;
|
||||
this.hideActions = newConfig.hideActions;
|
||||
|
||||
this.showNotification = true;
|
||||
|
||||
if (newConfig.timeout !== -1) {
|
||||
this.notificationTimeout = setTimeout(() => this.closeNotification(), newConfig.timeout ?? 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeNotification() {
|
||||
clearTimeout(this.notificationTimeout);
|
||||
|
||||
this.showNotification = false;
|
||||
this.notificationIcon = null;
|
||||
this.notificationText = null;
|
||||
this.notificationActions = null;
|
||||
this.hideActions = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../res/css/uwui-base.scss';
|
||||
@import '../res/css/colors.scss';
|
||||
@import '../res/css/font/overpass.css';
|
||||
@import '../res/css/font/overpass-mono.css';
|
||||
@import '../res/css/common.scss';
|
||||
|
||||
.uw-ultrawidify-container-root {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
|
||||
display: block !important;
|
||||
position: relative !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
pointer-events: none !important;
|
||||
|
||||
font-size: 16px !important;
|
||||
|
||||
.notification-popup {
|
||||
pointer-events: auto !important;
|
||||
position: absolute;
|
||||
z-index: 99999999;
|
||||
top: 2em;
|
||||
right: 2em;
|
||||
width: 32em;
|
||||
|
||||
padding: 0.7em 0.5em;
|
||||
|
||||
font-family: 'Overpass';
|
||||
|
||||
background-color: rgba(108, 55, 12, 0.779);
|
||||
color: #fff;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.notifcation-content {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.notification-text {
|
||||
text-align: justify;
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.25em;
|
||||
}
|
||||
|
||||
.notification-icon {
|
||||
font-size: 3em;
|
||||
line-height: 0.5;
|
||||
}
|
||||
.action-button {
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hide-actions {
|
||||
color: #ccc;
|
||||
font-size: 0.8em;
|
||||
justify-self: flex-end;
|
||||
align-self: flex-end;
|
||||
margin-top: 1em;
|
||||
margin-bottom: -1em;
|
||||
}
|
||||
|
||||
.hide-action-button {
|
||||
color: #eee;
|
||||
font-size: 0.9em;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: rgba(255,255,255,0.5);
|
||||
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -23,7 +23,15 @@ var ActionList = {
|
||||
name: 'Manually specify ratio',
|
||||
arg: AspectRatio.Fixed,
|
||||
customArg: true,
|
||||
hintHTML: '',
|
||||
customSetter: (value) => {
|
||||
const [width, height] = value.split(':');
|
||||
|
||||
if (width && height) {
|
||||
return +width / +height;
|
||||
}
|
||||
return +width;
|
||||
},
|
||||
hintHTML: '<small>Enter the aspect ratio as {width}:{height} or a single number, e.g. "21:9", "2.35:1", or "2.35" (without quotes).</small>',
|
||||
}],
|
||||
scopes: {
|
||||
global: false,
|
||||
|
@ -1,5 +1,5 @@
|
||||
if (process.env.CHANNEL !== 'stable') {
|
||||
console.log('Loaded BrowserDetect');
|
||||
console.info('Loaded BrowserDetect');
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ const BrowserDetect = {
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable') {
|
||||
console.log("Loading: BrowserDetect.js\n\nprocess.env.BROWSER:", process.env.BROWSER, "Exporting BrowserDetect:", BrowserDetect);
|
||||
console.info("BrowserDetect loaded:\n\nprocess.env.BROWSER:", process.env.BROWSER, "\nExporting BrowserDetect:", BrowserDetect);
|
||||
}
|
||||
|
||||
export default BrowserDetect;
|
@ -1,6 +1,6 @@
|
||||
if (process.env.CHANNEL !== 'stable') {
|
||||
console.log('We are not on stable channel. File init will be printed to console.');
|
||||
console.log('Loaded Debug.js');
|
||||
console.info('We are not on stable channel. File init will be printed to console.');
|
||||
console.info('Loading Debug.js');
|
||||
}
|
||||
|
||||
// Set prod to true when releasing
|
||||
@ -54,9 +54,13 @@ function __disableAllDebug(obj) {
|
||||
}
|
||||
}
|
||||
|
||||
if(Debug.debug)
|
||||
console.log("Guess we're debugging ultrawidify then. Debug.js must always load first, and others must follow.\nLoading: Debug.js");
|
||||
if (Debug.debug) {
|
||||
console.info("Guess we're debugging ultrawidify then. Debug.js must always load first, and others must follow.\nLoading: Debug.js");
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable') {
|
||||
console.info('Loaded Debug.js');
|
||||
}
|
||||
|
||||
|
||||
export default Debug;
|
@ -390,6 +390,22 @@ const ExtensionConfPatch = [
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
forVersion: '4.5.0',
|
||||
sites: {
|
||||
"www.wakanim.tv": {
|
||||
type: 'community',
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true,
|
||||
querySelectors: "#jwplayer-container",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -1193,6 +1193,18 @@ var ExtensionConf = {
|
||||
videoAlignment: 1,
|
||||
type: 'official',
|
||||
},
|
||||
"www.wakanim.tv": {
|
||||
type: 'community',
|
||||
DOM: {
|
||||
player: {
|
||||
manual: true,
|
||||
querySelectors: "#jwplayer-container",
|
||||
additionalCss: "",
|
||||
useRelativeAncestor: false,
|
||||
playerNodeCss: "",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import PlayerData from './video-data/PlayerData';
|
||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
||||
|
||||
if(process.env.CHANNEL !== 'stable'){
|
||||
console.log("Loading ActionHandler");
|
||||
console.info("Loading ActionHandler");
|
||||
}
|
||||
|
||||
class ActionHandler {
|
||||
@ -332,7 +332,7 @@ class ActionHandler {
|
||||
}
|
||||
|
||||
if(process.env.CHANNEL !== 'stable'){
|
||||
console.log("ActionHandler loaded");
|
||||
console.info("ActionHandler loaded");
|
||||
}
|
||||
|
||||
export default ActionHandler;
|
||||
|
@ -3,7 +3,7 @@ import { decycle } from 'json-cyclic';
|
||||
import Comms from './comms/Comms';
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.log('Loading Logger');
|
||||
console.info('Loading Logger');
|
||||
}
|
||||
|
||||
class Logger {
|
||||
@ -486,10 +486,14 @@ class Logger {
|
||||
|
||||
saveViaBgScript() {
|
||||
console.info('[info] will attempt to save. Issuing "show-logger"');
|
||||
if (!this.conf?.fileOptions?.enabled || this.isBackgroundScript) {
|
||||
console.info('[info] Logging to file is either disabled or we\'re not on the content script. Not saving.');
|
||||
if (this.isBackgroundScript) {
|
||||
console.info('[info] Background script cannot display logger UI.');
|
||||
return;
|
||||
}
|
||||
// if (!this.conf?.fileOptions?.enabled) {
|
||||
// console.info('[info] Logging to file is disabled. Logger won\'t get shown.');
|
||||
// return;
|
||||
// }
|
||||
|
||||
Comms.sendMessage({cmd: 'show-logger', forwardToSameFramePort: true, port: 'content-ui-port'});
|
||||
|
||||
@ -556,7 +560,7 @@ class Logger {
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.log('Logger loaded');
|
||||
console.info('Logger loaded');
|
||||
}
|
||||
|
||||
export default Logger;
|
||||
|
@ -9,7 +9,7 @@ import ExtensionConfPatch from '../conf/ExtConfPatches';
|
||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
||||
|
||||
if(process.env.CHANNEL !== 'stable'){
|
||||
console.log("Loading Settings");
|
||||
console.info("Loading Settings");
|
||||
}
|
||||
|
||||
class Settings {
|
||||
@ -589,3 +589,7 @@ class Settings {
|
||||
}
|
||||
|
||||
export default Settings;
|
||||
|
||||
if(process.env.CHANNEL !== 'stable'){
|
||||
console.info("Settings loaded");
|
||||
}
|
||||
|
@ -38,6 +38,9 @@ class ArDetector {
|
||||
this._nextTick = false;
|
||||
|
||||
this.canDoFallbackMode = false;
|
||||
|
||||
this.drmNotificationShown = false;
|
||||
|
||||
this.logger.log('info', 'init', `[ArDetector::ctor] creating new ArDetector. arid: ${this.arid}`);
|
||||
}
|
||||
|
||||
@ -506,6 +509,25 @@ class ArDetector {
|
||||
id = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether video we're trying to play is protected by DRM.
|
||||
*
|
||||
* When trying to get an image frame of a DRM-protected video in
|
||||
* firefox, the method canvas.drawImage(video) will throw an exception.
|
||||
*
|
||||
* This doesn't happen in Chrome. As opposed to Firefox, chrome will
|
||||
* simply draw a transparent black image and not tell anyone that
|
||||
* anything is amiss. However, since the image is (according to my testing
|
||||
* on netflix) completely transparent, this means we can determine whether
|
||||
* the video is DRM-protected by looking at the alpha byte of the image.
|
||||
*
|
||||
* (Videos don't tend to have an alpha channel, so they're always
|
||||
* completely opaque (i.e. have value of 255))
|
||||
*/
|
||||
hasDRM() {
|
||||
return this.blackframeContext.getImageData(0,0,1,1).data[3] === 0;
|
||||
}
|
||||
|
||||
frameCheck(){
|
||||
if(! this.video){
|
||||
this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`);
|
||||
@ -525,12 +547,19 @@ class ArDetector {
|
||||
//
|
||||
try {
|
||||
this.blackframeContext.drawImage(this.video, 0, 0, this.blackframeCanvas.width, this.blackframeCanvas.height);
|
||||
|
||||
// special browsers require special tests
|
||||
if (this.hasDRM()) {
|
||||
throw 'Video is protected by DRM. Autodetection cannot run here.';
|
||||
}
|
||||
this.fallbackMode = false;
|
||||
} catch (e) {
|
||||
this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] <@${this.arid}> %c[ArDetect::frameCheck] can't draw image on canvas. ${this.canDoFallbackMode ? 'Trying canvas.drawWindow instead' : 'Doing nothing as browser doesn\'t support fallback mode.'}`, "color:#000; backgroud:#f51;", e);
|
||||
|
||||
// nothing to see here, really, if fallback mode isn't supported by browser
|
||||
if (! this.canDoFallbackMode) {
|
||||
if (!this.drmNotificationShown) {
|
||||
this.drmNotificationShown = true;
|
||||
this.conf.player.showNotification('AARD_DRM');
|
||||
return;
|
||||
}
|
||||
if (! this.canvasReadyForDrawWindow()) {
|
||||
|
@ -2,7 +2,7 @@ import Debug from '../../conf/Debug';
|
||||
import BrowserDetect from '../../conf/BrowserDetect';
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.log("Loading Comms");
|
||||
console.info("Loading Comms");
|
||||
}
|
||||
|
||||
class Comms {
|
||||
@ -37,7 +37,7 @@ class Comms {
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.log("Comms loaded");
|
||||
console.info("Comms loaded");
|
||||
}
|
||||
|
||||
export default Comms;
|
||||
|
@ -2,7 +2,7 @@ import Debug from '../../conf/Debug';
|
||||
import BrowserDetect from '../../conf/BrowserDetect';
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.log("Loading CommsClient");
|
||||
console.info("Loading CommsClient");
|
||||
}
|
||||
|
||||
class CommsClient {
|
||||
@ -130,7 +130,7 @@ class CommsClient {
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.log("CommsClient loaded");
|
||||
console.info("CommsClient loaded");
|
||||
}
|
||||
|
||||
export default CommsClient;
|
||||
|
185
src/ext/lib/uwui/PlayerNotificationUI.js
Normal file
185
src/ext/lib/uwui/PlayerNotificationUI.js
Normal file
@ -0,0 +1,185 @@
|
||||
import UI from './UI';
|
||||
import VuexWebExtensions from 'vuex-webextensions';
|
||||
import NotificationUi from '../../../csui/NotificationUi.vue';
|
||||
import Notifications from '../../../common/data/notifications';
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.info("Loading: PlayerNotificationUi");
|
||||
}
|
||||
|
||||
let MuteScope = Object.freeze({
|
||||
CurrentSite: 'current-site',
|
||||
Global: 'global'
|
||||
});
|
||||
|
||||
class PlayerNotificationUi extends UI {
|
||||
|
||||
constructor (
|
||||
playerElement,
|
||||
settings
|
||||
) {
|
||||
super(
|
||||
'notification',
|
||||
PlayerNotificationUi.getStoreConfig(),
|
||||
PlayerNotificationUi.getUiConfig(playerElement),
|
||||
PlayerNotificationUi.getCommsConfig()
|
||||
);
|
||||
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
|
||||
//#region constructor helpers
|
||||
// we will move some things out of the constructor in order to keep things clean
|
||||
static getStoreConfig() {
|
||||
return {
|
||||
plugins: [
|
||||
VuexWebExtensions({
|
||||
persistentStates: [
|
||||
'notificationConfig'
|
||||
],
|
||||
}),
|
||||
],
|
||||
state: {
|
||||
// should be null by default!
|
||||
notificationConfig: {
|
||||
text: 'sample text <b>now with 100% more html formatting!</b>',
|
||||
icon: 'exclamation-circle',
|
||||
timeout: 5000,
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
'uw-set-notification'(state, payload) {
|
||||
console.log('mutation!', state, payload);
|
||||
state['notificationConfig'] = payload;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
'uw-set-notification'({commit}, payload) {
|
||||
console.log('action!', commit, payload);
|
||||
commit('uw-set-notification', payload);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static getUiConfig(playerElement) {
|
||||
return {
|
||||
parentElement: playerElement,
|
||||
component: NotificationUi
|
||||
}
|
||||
}
|
||||
|
||||
static getCommsConfig() {
|
||||
return {
|
||||
handlers: {
|
||||
'show-notification': [(message) => this.showNotification(message)],
|
||||
}
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region lifecycle
|
||||
replace(playerElement) {
|
||||
super.replace(this.getUiConfig(playerElement));
|
||||
}
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* Show notification on screen.
|
||||
*
|
||||
* @param notificationConfig notification config (or ID of notification config in /common/data/notifications.js)
|
||||
*
|
||||
* notificationConfig should resemble this:
|
||||
* {
|
||||
* timeout: number — how long we'll be displaying the notification. If empty, 10s. -1: until user dismisses it
|
||||
* icon: string — what icon we're gonna show. We're using bootstrap icons. Can be empty.
|
||||
* text: — notification text. Supports HTML.
|
||||
* notificationActions: [
|
||||
* {
|
||||
* command: function that gets executed upon clicking the button.
|
||||
* label: label of the button
|
||||
* icon: icon of the button
|
||||
* }
|
||||
* ],
|
||||
* hideActions: [
|
||||
* // more of notificationActions but with special case
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* When notificationConfig is a string, the function will add two additional notifications on the notificationActionsPile
|
||||
* * never show this notification ever again on any site
|
||||
* * never show this notification again on this site
|
||||
*/
|
||||
showNotification(notificationConfig) {
|
||||
if (typeof notificationConfig === 'string') {
|
||||
try {
|
||||
const config = Notifications[notificationConfig];
|
||||
|
||||
// this should _never_ appear on production version of the extension, but it should help with development.
|
||||
if (!config) {
|
||||
return this.vuexStore?.dispatch('uw-set-notification', {
|
||||
icon: 'x-circle-fill',
|
||||
text: `Notification for key ${notificationConfig} does not exist.`,
|
||||
timeout: -1,
|
||||
});
|
||||
}
|
||||
|
||||
// don't show notification if it's muted
|
||||
if (this.isNotificationMuted(notificationConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.vuexStore?.dispatch('uw-set-notification', {
|
||||
...config,
|
||||
hideActions: [
|
||||
{
|
||||
command: () => this.muteNotification(notificationConfig, MuteScope.CurrentSite),
|
||||
label: 'this site'
|
||||
},
|
||||
{
|
||||
command: () => this.muteNotification(notificationConfig, MuteScope.Global),
|
||||
label: 'never ever'
|
||||
}
|
||||
]
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('theres been an error:', e)
|
||||
}
|
||||
} else {
|
||||
this.vuexStore?.dispatch('uw-set-notification', notificationConfig);
|
||||
}
|
||||
}
|
||||
|
||||
muteNotification(notificationId, scope) {
|
||||
// ensure objects we try to set exist
|
||||
if (!this.settings.active.mutedNotifications) {
|
||||
this.settings.active.mutedNotifications = {};
|
||||
}
|
||||
if (!this.settings.active.mutedNotifications[notificationId]) {
|
||||
this.settings.active.mutedNotifications[notificationId] = {};
|
||||
}
|
||||
|
||||
// actually mute notification
|
||||
if (scope === MuteScope.Global) {
|
||||
this.settings.active.mutedNotifications[notificationId].$global = true;
|
||||
} else {
|
||||
this.settings.active.mutedNotifications[notificationId][window.location.hostname] = true;
|
||||
}
|
||||
|
||||
// persist settings
|
||||
this.settings.saveWithoutReload();
|
||||
}
|
||||
|
||||
isNotificationMuted(notificationId) {
|
||||
return this.settings.active.mutedNotifications?.[notificationId]?.$global
|
||||
|| this.settings.active.mutedNotifications?.[notificationId]?.[window.location.hostname];
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.info("PlayerNotificationUi loaded");
|
||||
}
|
||||
|
||||
|
||||
export default PlayerNotificationUi;
|
85
src/ext/lib/uwui/UI.js
Normal file
85
src/ext/lib/uwui/UI.js
Normal file
@ -0,0 +1,85 @@
|
||||
import { createApp } from 'vue';
|
||||
import { createStore } from 'vuex';
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.info("Loading: UI");
|
||||
}
|
||||
|
||||
|
||||
class UI {
|
||||
constructor(
|
||||
interfaceId,
|
||||
storeConfig,
|
||||
uiConfig, // {component, parentElement?}
|
||||
commsConfig,
|
||||
) {
|
||||
this.interfaceId = interfaceId;
|
||||
this.commsConfig = commsConfig;
|
||||
this.storeConfig = storeConfig,
|
||||
this.uiConfig = uiConfig;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
// initialize vuejs, but only once (check handled in initVue())
|
||||
// we need to initialize this _after_ initializing comms.
|
||||
|
||||
this.initVue();
|
||||
}
|
||||
|
||||
async initVue() {
|
||||
if (this.storeConfig) {
|
||||
this.vuexStore = createStore(this.storeConfig);
|
||||
}
|
||||
|
||||
this.initUi();
|
||||
}
|
||||
|
||||
async initUi() {
|
||||
const random = Math.round(Math.random() * 69420);
|
||||
const uwid = `uw-${this.interfaceId}-root-${random}`
|
||||
|
||||
const rootDiv = document.createElement('div');
|
||||
|
||||
rootDiv.setAttribute('style', `pointer-events: none; position: ${this.uiConfig.style?.position ?? 'absolute'}; width: ${this.uiConfig.style?.width ?? '100%'}; height: ${this.uiConfig.style?.height ?? '100%'}; top: ${this.uiConfig.style?.height ?? '0'}; ${this.uiConfig.additionalStyle ?? ''}`);
|
||||
rootDiv.setAttribute('id', uwid);
|
||||
rootDiv.classList.add('uw-ultrawidify-container-root');
|
||||
|
||||
if (this.uiConfig?.parentElement) {
|
||||
this.uiConfig.parentElement.appendChild(rootDiv);
|
||||
} else {
|
||||
document.body.appendChild(rootDiv);
|
||||
}
|
||||
|
||||
this.element = rootDiv;
|
||||
|
||||
const app = createApp(this.uiConfig.component);
|
||||
if (this.vuexStore) {
|
||||
app.use(this.vuexStore);
|
||||
}
|
||||
app.mount(`#${uwid}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces ui config and re-inits the UI
|
||||
* @param {*} newUiConfig
|
||||
*/
|
||||
replace(newUiConfig) {
|
||||
this.element?.remove();
|
||||
this.uiConfig = newUiConfig;
|
||||
this.initUi();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// this.comms?.destroy();
|
||||
this.element?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.info("UI.js loaded");
|
||||
}
|
||||
|
||||
|
||||
export default UI;
|
@ -5,7 +5,7 @@ import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
||||
import CropModePersistence from '../../../common/enums/crop-mode-persistence.enum';
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.log("Loading PageInfo");
|
||||
console.info("Loading PageInfo");
|
||||
}
|
||||
|
||||
class PageInfo {
|
||||
@ -58,22 +58,22 @@ class PageInfo {
|
||||
this.currentZoomScale = 1;
|
||||
}
|
||||
|
||||
injectCss(cssString) {
|
||||
this.comms.sendMessage({
|
||||
async injectCss(cssString) {
|
||||
await this.comms.sendMessage({
|
||||
cmd: 'inject-css',
|
||||
cssString: cssString
|
||||
});
|
||||
}
|
||||
|
||||
ejectCss(cssString) {
|
||||
this.comms.sendMessage({
|
||||
async ejectCss(cssString) {
|
||||
await this.comms.sendMessage({
|
||||
cmd: 'eject-css',
|
||||
cssString: cssString
|
||||
});
|
||||
}
|
||||
|
||||
replaceCss(oldCssString, newCssString) {
|
||||
this.comms.sendMessage({
|
||||
async replaceCss(oldCssString, newCssString) {
|
||||
await this.comms.sendMessage({
|
||||
cmd: 'replace-css',
|
||||
newCssString,
|
||||
oldCssString
|
||||
@ -623,4 +623,8 @@ class PageInfo {
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.info("PageInfo loaded!");
|
||||
}
|
||||
|
||||
export default PageInfo;
|
||||
|
@ -1,9 +1,11 @@
|
||||
import Debug from '../../conf/Debug';
|
||||
import ExtensionMode from '../../../common/enums/extension-mode.enum'
|
||||
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
||||
import PlayerNotificationUi from '../uwui/PlayerNotificationUI';
|
||||
|
||||
if(Debug.debug)
|
||||
console.log("Loading: PlayerData.js");
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.info("Loading: PlayerData.js");
|
||||
}
|
||||
|
||||
/* sprejme <video> tag (element) in seznam imen, ki se lahko pojavijo v razredih oz. id staršev.
|
||||
// vrne dimenzije predvajalnika (širina, višina)
|
||||
@ -42,6 +44,7 @@ class PlayerData {
|
||||
this.extensionMode = videoData.extensionMode;
|
||||
this.invalid = false;
|
||||
this.element = this.getPlayer();
|
||||
this.notificationService = new PlayerNotificationUi(this.element, this.settings);
|
||||
this.dimensions = undefined;
|
||||
this.overlayNode = undefined;
|
||||
|
||||
@ -62,6 +65,7 @@ class PlayerData {
|
||||
this.checkPlayerSizeChange();
|
||||
}
|
||||
this.startChangeDetection();
|
||||
|
||||
} catch (e) {
|
||||
console.error('[Ultrawidify::PlayerData::ctor] There was an error setting up player data. You should be never seeing this message. Error:', e);
|
||||
this.invalid = true;
|
||||
@ -72,7 +76,6 @@ class PlayerData {
|
||||
return new Promise( (resolve, reject) => setTimeout(() => resolve(), timeout));
|
||||
}
|
||||
|
||||
|
||||
static isFullScreen(){
|
||||
return ( window.innerHeight == window.screen.height && window.innerWidth == window.screen.width);
|
||||
}
|
||||
@ -97,6 +100,7 @@ class PlayerData {
|
||||
destroy() {
|
||||
this.stopChangeDetection();
|
||||
this.destroyOverlay();
|
||||
this.notificationService?.destroy();
|
||||
}
|
||||
|
||||
startChangeDetection(){
|
||||
@ -222,6 +226,10 @@ class PlayerData {
|
||||
// is defined. Since resizer needs a PlayerData object to exist, videoData.resizer will
|
||||
// be undefined the first time this function will run.
|
||||
this.videoData.resizer?.restore();
|
||||
|
||||
// NOTE: it's possible that notificationService hasn't been initialized yet at this point.
|
||||
// no biggie if it wasn't, we just won't replace the notification UI
|
||||
this.notificationService?.replace(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,6 +472,14 @@ class PlayerData {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
showNotification(notificationId) {
|
||||
this.notificationService?.showNotification(notificationId);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.CHANNEL !== 'stable'){
|
||||
console.info("PlayerData loaded");
|
||||
}
|
||||
|
||||
export default PlayerData;
|
||||
|
@ -6,6 +6,7 @@ import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
||||
|
||||
class VideoData {
|
||||
|
||||
|
||||
constructor(video, settings, pageInfo){
|
||||
this.vdid = (Math.random()*100).toFixed();
|
||||
this.logger = pageInfo.logger;
|
||||
@ -27,16 +28,7 @@ class VideoData {
|
||||
height: this.video.offsetHeight,
|
||||
};
|
||||
|
||||
// this is in case extension loads before the video
|
||||
video.addEventListener('loadeddata', () => {
|
||||
this.logger.log('info', 'init', '[VideoData::ctor->video.onloadeddata] Video fired event "loaded data!"');
|
||||
this.onVideoLoaded();
|
||||
});
|
||||
|
||||
// this one is in case extension loads after the video is loaded
|
||||
video.addEventListener('timeupdate', () => {
|
||||
this.onVideoLoaded();
|
||||
});
|
||||
this.setupStageOne();
|
||||
}
|
||||
|
||||
async onVideoLoaded() {
|
||||
@ -60,6 +52,41 @@ class VideoData {
|
||||
}
|
||||
}
|
||||
|
||||
async injectBaseCss() {
|
||||
try {
|
||||
await this.pageInfo.injectCss(`
|
||||
.uw-ultrawidify-base-wide-screen {
|
||||
margin: 0px 0px 0px 0px !important; width: initial !important; align-self: start !important; justify-self: start !important;
|
||||
}
|
||||
`);
|
||||
} catch (e) {
|
||||
console.error('Failed to inject base css!', e);
|
||||
}
|
||||
}
|
||||
|
||||
unsetBaseClass() {
|
||||
this.video.classList.remove('uw-ultrawidify-base-wide-screen');
|
||||
}
|
||||
|
||||
async setupStageOne() {
|
||||
this.logger.log('info', 'init', '%c[VideoData::setupStageOne] ——————————— Starting setup stage one! ———————————', 'color: #0f9');
|
||||
// ensure base css is loaded before doing anything
|
||||
this.injectBaseCss();
|
||||
|
||||
// this is in case extension loads before the video
|
||||
this.video.addEventListener('loadeddata', () => {
|
||||
this.logger.log('info', 'init', '[VideoData::ctor->video.onloadeddata] Video fired event "loaded data!"');
|
||||
this.onVideoLoaded();
|
||||
});
|
||||
|
||||
// this one is in case extension loads after the video is loaded
|
||||
this.video.addEventListener('timeupdate', () => {
|
||||
this.onVideoLoaded();
|
||||
});
|
||||
|
||||
this.logger.log('info', 'init', '%c[VideoData::setupStageOne] ——————————— Setup stage one complete! ———————————', 'color: #0f9');
|
||||
}
|
||||
|
||||
async setupStageTwo() {
|
||||
// POZOR: VRSTNI RED JE POMEMBEN (arDetect mora bit zadnji)
|
||||
// NOTE: ORDERING OF OBJ INITIALIZATIONS IS IMPORTANT (arDetect needs to go last)
|
||||
@ -98,8 +125,13 @@ class VideoData {
|
||||
this.logger.log('info', ['debug', 'init'], '[VideoData::ctor] Created videoData with vdid', this.vdid, '\nextension mode:', this.extensionMode)
|
||||
|
||||
this.pageInfo.initMouseActionHandler(this);
|
||||
|
||||
// NOTE — since base class for our <video> element depends on player aspect ratio,
|
||||
// we handle it in PlayerData class.
|
||||
this.video.classList.add('uw-ultrawidify-base-wide-screen');
|
||||
this.video.classList.add(this.userCssClassName); // this also needs to be applied BEFORE we initialize resizer!
|
||||
|
||||
|
||||
// start fallback video/player size detection
|
||||
this.fallbackChangeDetection();
|
||||
|
||||
@ -123,6 +155,7 @@ class VideoData {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
restoreCrop() {
|
||||
this.logger.log('info', 'debug', '[VideoData::restoreCrop] Attempting to reset aspect ratio.')
|
||||
// if we have default crop set for this page, apply this.
|
||||
@ -175,7 +208,8 @@ class VideoData {
|
||||
// we still only need to make sure we're only adding our class to classlist if it has been
|
||||
// removed. classList.add() will _still_ trigger mutation (even if classlist wouldn't change).
|
||||
// This is a problem because INFINITE RECURSION TIME, and we _really_ don't want that.
|
||||
context.video.classList.add(this.userCssClassName);
|
||||
context.video.classList.add(this.userCssClassName);
|
||||
context.video.classList.add('uw-ultrawidify-base-wide-screen');
|
||||
}
|
||||
// always trigger refresh on class changes, since change of classname might trigger change
|
||||
// of the player size as well.
|
||||
@ -298,6 +332,7 @@ class VideoData {
|
||||
|
||||
if (this.video) {
|
||||
this.video.classList.remove(this.userCssClassName);
|
||||
this.video.classList.remove('uw-ultrawidify-base-wide-screen');
|
||||
}
|
||||
|
||||
this.pause();
|
||||
|
@ -670,10 +670,6 @@ class Resizer {
|
||||
|
||||
const styleArray = this.buildStyleArray('', extraStyleString)
|
||||
|
||||
// sometimes, site designers will center <video> by setting margin: to something. We do not like that, as
|
||||
// it prevents extension from working properly.
|
||||
styleArray.push('margin: 0px 0px 0px 0px !important; width: initial !important; height: initial !important');
|
||||
|
||||
// add remaining elements
|
||||
if (stretchFactors) {
|
||||
styleArray.push(`transform: translate(${translate.x}px, ${translate.y}px) scale(${stretchFactors.xFactor}, ${stretchFactors.yFactor}) !important;`);
|
||||
|
@ -1,6 +1,6 @@
|
||||
// vue dependency imports
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { createApp } from 'vue';
|
||||
import { createStore } from 'vuex';
|
||||
import VuexWebExtensions from 'vuex-webextensions';
|
||||
import LoggerUi from '../csui/LoggerUi';
|
||||
|
||||
@ -123,17 +123,16 @@ class UwUi {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Vue.prototype.$browser = global.browser;
|
||||
Vue.use(Vuex);
|
||||
this.vuexStore = new Vuex.Store({
|
||||
plugins: [VuexWebExtensions({
|
||||
persistentStates: [
|
||||
'uwLog',
|
||||
'showLogger',
|
||||
'loggingEnded',
|
||||
],
|
||||
})],
|
||||
this.vuexStore = createStore({
|
||||
plugins: [
|
||||
VuexWebExtensions({
|
||||
persistentStates: [
|
||||
'uwLog',
|
||||
'showLogger',
|
||||
'loggingEnded',
|
||||
],
|
||||
}),
|
||||
],
|
||||
state: {
|
||||
uwLog: '',
|
||||
showLogger: false,
|
||||
@ -168,9 +167,6 @@ class UwUi {
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Ultrawidify failed to initialize vue. Error:", e);
|
||||
}
|
||||
|
||||
// make sure we don't init twice
|
||||
this.vueInitiated = true;
|
||||
@ -184,22 +180,27 @@ class UwUi {
|
||||
const uwid = `uw-ui-root-${random}`;
|
||||
|
||||
const rootDiv = document.createElement('div');
|
||||
rootDiv.setAttribute("style", "position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; background-color: #ff0000;");
|
||||
rootDiv.setAttribute("style", "position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; pointer-events: none");
|
||||
rootDiv.setAttribute("id", uwid);
|
||||
rootDiv.classList.add('uw-ultrawidify-container-root');
|
||||
|
||||
document.body.appendChild(rootDiv);
|
||||
|
||||
try {
|
||||
new Vue({
|
||||
el: `#${uwid}`,
|
||||
components: {
|
||||
LoggerUi: LoggerUi
|
||||
},
|
||||
store: this.vuexStore,
|
||||
render(h) {
|
||||
return h('logger-ui');
|
||||
}
|
||||
});
|
||||
createApp(LoggerUi)
|
||||
.use(this.vuexStore)
|
||||
.mount(`#${uwid}`);
|
||||
|
||||
// new Vue({
|
||||
// el: `#${uwid}`,
|
||||
// components: {
|
||||
// LoggerUi: LoggerUi
|
||||
// },
|
||||
// store: this.vuexStore,
|
||||
// render(h) {
|
||||
// return h('logger-ui');
|
||||
// }
|
||||
// });
|
||||
} catch (e) {
|
||||
console.error("Error while initiating vue:", e)
|
||||
}
|
||||
@ -208,12 +209,14 @@ class UwUi {
|
||||
}
|
||||
|
||||
async showLogger() {
|
||||
console.log("show logger?")
|
||||
if (!this.loggerUiInitiated) {
|
||||
await this.initLoggerUi();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
console.log("will show logger")
|
||||
this.vuexStore.dispatch('uw-show-logger');
|
||||
} catch (e) {
|
||||
console.error('Failed to dispatch vuex store', e)
|
||||
|
@ -9,17 +9,17 @@ import PageInfo from './lib/video-data/PageInfo';
|
||||
import Logger from './lib/Logger';
|
||||
|
||||
|
||||
if(Debug.debug){
|
||||
console.log("\n\n\n\n\n\n ——— Sᴛλʀᴛɪɴɢ Uʟᴛʀᴀᴡɪᴅɪꜰʏ ———\n << ʟᴏᴀᴅɪɴɢ ᴍᴀɪɴ ꜰɪʟᴇ >>\n\n\n\n");
|
||||
if(process.env.CHANNEL !== 'stable'){
|
||||
console.warn("\n\n\n\n\n\n ——— Sᴛλʀᴛɪɴɢ Uʟᴛʀᴀᴡɪᴅɪꜰʏ ———\n << ʟᴏᴀᴅɪɴɢ ᴍᴀɪɴ ꜰɪʟᴇ >>\n\n\n\n");
|
||||
try {
|
||||
if(window.self !== window.top){
|
||||
console.log("%cWe aren't in an iframe.", "color: #afc, background: #174");
|
||||
console.info("%cWe aren't in an iframe.", "color: #afc, background: #174");
|
||||
}
|
||||
else{
|
||||
console.log("%cWe are in an iframe!", "color: #fea, background: #d31", window.self, window.top);
|
||||
console.info("%cWe are in an iframe!", "color: #fea, background: #d31", window.self, window.top);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("%cWe are in an iframe!", "color: #fea, background: #d31");
|
||||
console.info("%cWe are in an iframe!", "color: #fea, background: #d31");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "Ultrawidify",
|
||||
"description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.",
|
||||
"version": "4.4.10",
|
||||
"version": "4.5.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "{cf02b1a7-a01a-4e37-a609-516a283f1ed3}"
|
||||
@ -19,11 +19,13 @@
|
||||
|
||||
"content_scripts": [{
|
||||
"matches": ["*://*/*"],
|
||||
"js": [
|
||||
"ext/uw.js"
|
||||
"js": [
|
||||
"common/lib/browser-polyfill.js",
|
||||
"ext/uw.js"
|
||||
],
|
||||
"css": [
|
||||
"ext/uw-ui.css"
|
||||
"ext/uw-ui.css",
|
||||
"ext/uw.css"
|
||||
],
|
||||
"all_frames": true
|
||||
}],
|
||||
@ -47,7 +49,8 @@
|
||||
"ext/*",
|
||||
"res/fonts/*",
|
||||
"res/css/*",
|
||||
"res/img/settings/about-bg.png"
|
||||
"res/img/settings/about-bg.png",
|
||||
"res/icons/*"
|
||||
],
|
||||
"permissions": [
|
||||
"storage",
|
||||
|
@ -213,6 +213,7 @@ export default {
|
||||
|
||||
try {
|
||||
if (BrowserDetect.firefox) {
|
||||
// reminder — webextension-polyfill doesn't seem to work in vue!
|
||||
await browser.permissions.request({permissions: ['downloads']});
|
||||
browser.downloads.download({saveAs: true, filename: 'ultrawidify-settings.json', url: fileUrl});
|
||||
} else if (BrowserDetect.chrome) {
|
||||
|
@ -38,12 +38,14 @@ import Settings from '../ext/lib/Settings';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
addonVersion: Settings.getVersion(),
|
||||
addonVersion: '[extension version not loaded. This is a bug.]',
|
||||
mailtoLink: 'mailto:tamius.han@gmail.com',
|
||||
redditLink: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.addonVersion = Settings.getExtensionVersion();
|
||||
|
||||
const messageTemplate = encodeURIComponent(
|
||||
`Describe your issue in more detail. In case of misaligned videos, please provide screenshots. When reporting\
|
||||
issues with autodetection not detecting aspect ratio correctly, please provide a link with timestamp to the\
|
||||
|
@ -49,8 +49,8 @@
|
||||
</div>
|
||||
|
||||
<!-- CUSTOM ARGUMENT INPUT -->
|
||||
<div v-if="selectedArgument && selectedArgument.customArg"
|
||||
class="flex flex-row">
|
||||
<div v-if="selectedArgument && selectedArgument.customArg">
|
||||
<div class="flex flex-row">
|
||||
<div class="flex label-secondary form-label">
|
||||
<span class="w100">
|
||||
{{selectedArgument.name}}:
|
||||
@ -59,9 +59,15 @@
|
||||
<div class="flex flex-grow flex-input">
|
||||
<input type="text"
|
||||
class="w100"
|
||||
v-model="customArgumentValue"
|
||||
:value="customArgumentValue"
|
||||
@input="setCustomValue($event.target.value, selectedArgument.customSetter)"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
<div v-if="selectedArgument.hintHTML" v-html="selectedArgument.hintHTML">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -122,6 +128,13 @@ export default {
|
||||
this.selectedArgument = ActionList[this.selectedAction].args.find(x => x.arg == arg);
|
||||
this.customArgumentValue = undefined;
|
||||
},
|
||||
setCustomValue(value, customSetter) {
|
||||
if (!customSetter) {
|
||||
this.customArgumentValue = value;
|
||||
} else {
|
||||
this.customArgumentValue = customSetter(value);
|
||||
}
|
||||
},
|
||||
emitCommand() {
|
||||
this.$emit(
|
||||
'set-command',
|
||||
|
@ -8,7 +8,7 @@
|
||||
<!-- Load some resources only in development environment -->
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<body class="uw-ultrawidify-container-root">
|
||||
<div id="app"></div>
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
|
@ -1,8 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import { createApp } from 'vue';
|
||||
import App from './App';
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
render: h => h(App)
|
||||
})
|
||||
createApp(App).mount('#app');
|
||||
|
@ -1,17 +1,41 @@
|
||||
<template>
|
||||
<!--
|
||||
NOTE — the code that makes ultrawidify popup work in firefox regardless of whether the
|
||||
extension is being displayed in a normal or a small/overflow popup breaks the popup
|
||||
behaviour on Chrome (where the popup would never reach the full width of 800px)
|
||||
|
||||
Since I'm tired and the hour is getting late, we'll just add an extra CSS class for
|
||||
non-firefox builds of this extension and be done with it. No need to complicate things
|
||||
further than that.
|
||||
-->
|
||||
<div v-if="settingsInitialized"
|
||||
class="popup flex flex-column no-overflow"
|
||||
:class="{'popup-chrome': ! BrowserDetect.firefox}"
|
||||
>
|
||||
<div class="header flex-row flex-nogrow flex-noshrink relative">
|
||||
<div class="flex-row flex-nogrow flex-noshrink relative"
|
||||
:class="{'header': !narrowPopup, 'header-small': narrowPopup}"
|
||||
>
|
||||
<span class="smallcaps">Ultrawidify</span>: <small>Quick settings</small>
|
||||
<div class="absolute channel-info" v-if="BrowserDetect.processEnvChannel !== 'stable'">
|
||||
Build channel: {{BrowserDetect.processEnvChannel}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="narrowPopup"
|
||||
class="w100 show-more flex flex-row flex-center flex-cross-center menu-button"
|
||||
@click="toggleSideMenu()"
|
||||
>
|
||||
<Icon v-if="!sideMenuVisible" icon="list" />
|
||||
<Icon v-else icon="x" />
|
||||
<div>Menu</div>
|
||||
</div>
|
||||
<div class="flex flex-row body no-overflow flex-grow">
|
||||
<!-- TABS/SIDEBAR -->
|
||||
<div id="tablist" class="flex flex-column flex-nogrow flex-noshrink h100">
|
||||
<div id="tablist"
|
||||
v-show="!narrowPopup || sideMenuVisible"
|
||||
class="flex flex-column flex-nogrow flex-noshrink h100"
|
||||
:class="{'w100': narrowPopup}"
|
||||
>
|
||||
<div class="menu-item"
|
||||
:class="{'selected-tab': selectedTab === 'global'}"
|
||||
@click="selectTab('global')"
|
||||
@ -29,7 +53,7 @@
|
||||
<div class="">
|
||||
Site settings
|
||||
</div>
|
||||
<div v-if="selectedTab === 'site' && this.activeSites.length > 1"
|
||||
<div v-if="selectedTab === 'site' && activeSites.length > 1"
|
||||
class=""
|
||||
>
|
||||
<small>Select site to control:</small>
|
||||
@ -55,7 +79,7 @@
|
||||
<div class="">
|
||||
Video settings <span v-if="canShowVideoTab.canShow && canShowVideoTab.warning" class="warning-color">⚠</span>
|
||||
</div>
|
||||
<div v-if="selectedTab === 'video' && this.activeFrames.length > 0"
|
||||
<div v-if="selectedTab === 'video' && activeFrames.length > 0"
|
||||
class=""
|
||||
>
|
||||
<small>Select embedded frame to control:</small>
|
||||
@ -82,7 +106,7 @@
|
||||
<div class="">
|
||||
Advanced settings
|
||||
</div>
|
||||
<div v-if="selectedTab === 'site-details' && this.activeSites.length > 1"
|
||||
<div v-if="selectedTab === 'site-details' && activeSites.length > 1"
|
||||
class=""
|
||||
>
|
||||
<small>Select site to control:</small>
|
||||
@ -136,8 +160,12 @@
|
||||
</div>
|
||||
|
||||
<!-- PANELS/CONTENT -->
|
||||
<div id="tab-content" class="flex-grow h100 overflow-y-auto">
|
||||
<VideoPanel v-if="settings && settings.active && selectedTab === 'video'"
|
||||
<div id="tab-content"
|
||||
v-show="!(narrowPopup && sideMenuVisible)"
|
||||
class="flex-grow h100 overflow-y-auto"
|
||||
:class="{'narrow-content': narrowPopup}"
|
||||
>
|
||||
<VideoPanel v-if="settings?.active && selectedTab === 'video'"
|
||||
class=""
|
||||
:someSitesDisabledWarning="canShowVideoTab.warning"
|
||||
:settings="settings"
|
||||
@ -145,7 +173,7 @@
|
||||
:zoom="currentZoom"
|
||||
@zoom-change="updateZoom($event)"
|
||||
/>
|
||||
<DefaultSettingsPanel v-if="settings && settings.active && (selectedTab === 'site' || selectedTab === 'global')"
|
||||
<DefaultSettingsPanel v-if="settings?.active && (selectedTab === 'global' || selectedTab === 'site')"
|
||||
class=""
|
||||
:settings="settings"
|
||||
:scope="selectedTab"
|
||||
@ -167,6 +195,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Icon from '../common/components/Icon.vue'
|
||||
import WhatsNewPanel from './panels/WhatsNewPanel.vue';
|
||||
import SiteDetailsPanel from './panels/SiteDetailsPanel.vue';
|
||||
import Donate from '../common/misc/Donate.vue';
|
||||
@ -206,6 +235,8 @@ export default {
|
||||
canShowVideoTab: {canShow: true, warning: true},
|
||||
showWhatsNew: false,
|
||||
BrowserDetect: BrowserDetect,
|
||||
narrowPopup: null,
|
||||
sideMenuVisible: null,
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
@ -245,6 +276,20 @@ export default {
|
||||
await this.sleep(5000);
|
||||
}
|
||||
},
|
||||
async updated() {
|
||||
const body = document.getElementsByTagName('body')[0];
|
||||
|
||||
// ensure that narrowPopup only gets set the first time the popup renders
|
||||
// if popup was rendered before, we don't do anything because otherwise
|
||||
// we'll be causing an unwanted re-render
|
||||
//
|
||||
// another thing worth noting — the popup gets first initialized with
|
||||
// offsetWidth set to 0. This means proper popup will be displayed as a
|
||||
// mini popup if we don't check for that.
|
||||
if (this.narrowPopup === null && body.offsetWidth > 0) {
|
||||
this.narrowPopup = body.offsetWidth < 600;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
VideoPanel,
|
||||
DefaultSettingsPanel,
|
||||
@ -255,6 +300,7 @@ export default {
|
||||
Donate,
|
||||
SiteDetailsPanel,
|
||||
WhatsNewPanel,
|
||||
Icon,
|
||||
},
|
||||
methods: {
|
||||
async sleep(t) {
|
||||
@ -282,6 +328,7 @@ export default {
|
||||
this.settings.active.whatsNewChecked = true;
|
||||
this.settings.save();
|
||||
}
|
||||
this.toggleSideMenu(false);
|
||||
},
|
||||
selectFrame(frame) {
|
||||
this.selectedFrame = frame;
|
||||
@ -325,6 +372,7 @@ export default {
|
||||
if (!this.site || this.site.host !== message.site.host) {
|
||||
this.port.postMessage({cmd: 'get-current-zoom'});
|
||||
}
|
||||
console.log("processing received message:", message)
|
||||
this.site = message.site;
|
||||
|
||||
// update activeSites
|
||||
@ -475,6 +523,10 @@ export default {
|
||||
},
|
||||
selectSite(host) {
|
||||
this.selectedSite = host;
|
||||
},
|
||||
toggleSideMenu(visible) {
|
||||
console.warn('toggling side menu visible to:', visible ?? !this.sideMenuVisible, "arg:", visible )
|
||||
this.sideMenuVisible = visible ?? !this.sideMenuVisible;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -486,13 +538,22 @@ export default {
|
||||
<style src="../res/css/common.scss"></style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
html, body {
|
||||
width: 800px !important;
|
||||
max-width: 800px !important;
|
||||
html {
|
||||
// width: 800px !important;
|
||||
// max-width: 800px !important;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.zero-width {
|
||||
width: 0px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header .header-small, .narrow-content {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
#tablist {
|
||||
min-width: 275px;
|
||||
max-width: 300px;
|
||||
@ -509,6 +570,17 @@ html, body {
|
||||
padding-bottom: 1px;
|
||||
font-size: 2.7em;
|
||||
}
|
||||
.header-small {
|
||||
overflow: hidden;
|
||||
background-color: #7f1416;
|
||||
color: #fff;
|
||||
margin: 0px;
|
||||
margin-top: 0px;
|
||||
padding-top: 8px;
|
||||
padding-left: 15px;
|
||||
padding-bottom: 1px;
|
||||
font-size: 1.27em;
|
||||
}
|
||||
|
||||
|
||||
.menu-item-inline-desc{
|
||||
@ -575,6 +647,7 @@ html, body {
|
||||
background-color: initial;
|
||||
border-left: #f0c089 3px solid !important;
|
||||
}
|
||||
|
||||
.tabitem-selected::before {
|
||||
padding-right: 8px;
|
||||
}
|
||||
@ -588,12 +661,37 @@ html, body {
|
||||
padding-left: 0.33em;
|
||||
}
|
||||
|
||||
.menu-button {
|
||||
margin-bottom: 4px;
|
||||
padding: 4px;
|
||||
border-bottom: #f18810 1px solid !important;
|
||||
font-size: 1.5rem !important;
|
||||
cursor: pointer;
|
||||
user-select: none;;
|
||||
}
|
||||
|
||||
.popup {
|
||||
// max-width: 780px;
|
||||
// width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
/**
|
||||
This was written at the top, but it's worth repeating.
|
||||
|
||||
NOTE — the code that makes ultrawidify popup work in firefox regardless of whether the
|
||||
extension is being displayed in a normal or a small/overflow popup breaks the popup
|
||||
behaviour on Chrome (where the popup would never reach the full width of 800px)
|
||||
|
||||
Since I'm tired and the hour is getting late, we'll just add an extra CSS class for
|
||||
non-firefox builds of this extension and be done with it. No need to complicate things
|
||||
further than that.
|
||||
|
||||
It also seems that Chrome doesn't like if we set the width of the popup all the way to
|
||||
800px (probably something something scrollbar), so let's just take away a few px.
|
||||
*/
|
||||
.popup-chrome {
|
||||
width: 780px !important;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
@ -606,4 +704,9 @@ html, body {
|
||||
bottom: 0.85rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.show-more {
|
||||
padding-top: 12px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -47,7 +47,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
addonVersion: browser.runtime.getManifest().version || chrome.runtime.getManifest().version,
|
||||
// reminder — webextension-polyfill doesn't seem to work in vue!
|
||||
addonVersion: BrowserDetect.firefox ? browser.runtime.getManifest().version : chrome.runtime.getManifest().version,
|
||||
loggingEnabled: false,
|
||||
loggerSettings: '',
|
||||
loggerSettingsError: false,
|
||||
|
@ -147,7 +147,6 @@ export default {
|
||||
components: {
|
||||
ShortcutButton,
|
||||
},
|
||||
|
||||
watch: {
|
||||
settings: {
|
||||
deep: true,
|
||||
@ -172,7 +171,6 @@ export default {
|
||||
let site;
|
||||
if (this.scope === 'global') {
|
||||
site = '@global'
|
||||
this.site = site;
|
||||
} else {
|
||||
if (!this.site) {
|
||||
return '';
|
||||
|
@ -2,19 +2,27 @@
|
||||
<div>
|
||||
<h2>What's new</h2>
|
||||
<p>Full changelog for older versions <a href="https://github.com/tamius-han/ultrawidify/blob/master/CHANGELOG.md">is available here</a>.</p>
|
||||
<p class="label">4.4.10</p>
|
||||
<p class="label">4.5.0</p>
|
||||
<ul>
|
||||
<li>
|
||||
Video alignment should now work on Twitch (<a href="https://github.com/tamius-han/ultrawidify/issues/109">#109</a>)
|
||||
Under the hood: migrated from vue2 to vue3, because optional chaining in templates is too OP.
|
||||
</li>
|
||||
<li>
|
||||
Videos should now align properly on Hulu while cropped (<a href="https://github.com/tamius-han/ultrawidify/issues/111">#111 & via email</a>)
|
||||
(On options page, section 'Action & shortcuts') Manual aspect ratio now supports entering custom ratios using '21/9' and '2.39:1' formats (as opposed to single number, e.g. '2.39') — <a href="https://github.com/tamius-han/ultrawidify/issues/121">#121</a>.
|
||||
</li>
|
||||
<li>
|
||||
Fixed a problem where changing certain settings would cause multiple instances of Ultrawidify to run on a page, effectively preventing some crop options to be set until reload. (possibly <a href="(https://github.com/tamius-han/ultrawidify/issues/112">#112</a>?)
|
||||
Added config for wakanim.tv (special thanks to <a href="https://github.com/saschanaz">@saschanaz</a> for doing the legwork — <a href="https://github.com/tamius-han/ultrawidify/issues/113">#113</a>)
|
||||
</li>
|
||||
<li>
|
||||
Fixed a problem where embedded videos would be misaligned after switching from full screen
|
||||
<li>
|
||||
(In Firefox) When extension was placed in overflow menu, the popup was cut off. That should be fixed now. (<a href="https://github.com/tamius-han/ultrawidify/issues/119">#119</a>)
|
||||
</li>
|
||||
<li>
|
||||
The extension will now show a notification when autodetection can't run due to DRM.
|
||||
</li>
|
||||
<li>
|
||||
Videos on facebook and reddit no longer get shifted up and to the left for me (cropping most of the video off-screen), but I haven't been
|
||||
deliberately trying to fix that issue. If you experience that issue, please consider contacting me (via github or email) with a link to a
|
||||
problematic video.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<!-- Load some resources only in development environment -->
|
||||
<% } %>
|
||||
</head>
|
||||
<body style="width: 800px; height: 600px; overflow: hidden !important">
|
||||
<body style="width: 100%; height: 100%; overflow-x: hidden;" class="uw-ultrawidify-container-root">
|
||||
<div id="app">
|
||||
|
||||
</div>
|
||||
|
@ -1,11 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import { createApp } from 'vue'
|
||||
import App from './App';
|
||||
|
||||
// global.browser = require('webextension-polyfill')
|
||||
// Vue.prototype.$browser = global.browser
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
render: h => h(App)
|
||||
})
|
||||
createApp(App).mount('#app');
|
||||
|
@ -17,7 +17,7 @@ body {
|
||||
}
|
||||
|
||||
/* STANDARD WIDTHS AND HEIGHTS */
|
||||
|
||||
.uw-ultrawidify-container-root {
|
||||
.w100 {
|
||||
width: 100%;
|
||||
}
|
||||
@ -99,294 +99,293 @@ body {
|
||||
.description {
|
||||
color: $text-dim;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding-top: 1.5rem;
|
||||
font-size: 1.5rem;
|
||||
font-variant: small-caps;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.label-secondary {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.button-box {
|
||||
padding-top: 0.69rem;
|
||||
padding-bottom: 0.69rem;
|
||||
}
|
||||
|
||||
.indent {
|
||||
padding-left: 4.2rem;
|
||||
}
|
||||
|
||||
.row-padding {
|
||||
padding-top: 0.69rem;
|
||||
padding-bottom: 0.69rem;
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color: $primary-color;
|
||||
}
|
||||
a:hover {
|
||||
color: lighten($primary-color, 10%);
|
||||
}
|
||||
|
||||
/* INPUT FORMATTING */
|
||||
input[type="number"], input[type="text"], input {
|
||||
outline: none;
|
||||
background-color: $input-background;
|
||||
color: $text-normal;
|
||||
padding: 0.1rem;
|
||||
padding-top: 0.2rem;
|
||||
padding-bottom: 0.1rem;
|
||||
margin-left: 1rem;
|
||||
border: 1px solid $input-border;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
background: #444444;
|
||||
color: darken($text-normal, 50%);
|
||||
}
|
||||
|
||||
/* ELEMENT POSITIONING */
|
||||
|
||||
.row {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.float-left {
|
||||
float: left;
|
||||
}
|
||||
.float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* TEXT FORMATTING (no colors) */
|
||||
|
||||
small {
|
||||
font-size: 0.75em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 0.75em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.medium-small {
|
||||
font-size: 0.85em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.smallcaps{
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
.center{
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.invalid-input {
|
||||
border: 1px solid #720 !important;
|
||||
background-color: #410 !important;
|
||||
}
|
||||
|
||||
.button {
|
||||
/*display: inline-block;*/
|
||||
// padding-top: 8px;
|
||||
// padding-bottom: 3px;
|
||||
//padding-left: 5px;
|
||||
//padding-right: 5px;
|
||||
border: 1px solid rgb(39, 39, 39);
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
color: $text-dim;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.selected, .setting-selected {
|
||||
color: $selected-color !important;
|
||||
background-color: $background-selected !important;
|
||||
}
|
||||
|
||||
.selected-tab {
|
||||
color: $selected-color;
|
||||
}
|
||||
|
||||
.setting-selected {
|
||||
border: 1px solid shade($selected-color, 25%);
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
color: lighten($selected-color, 10%);
|
||||
background-color: lighten($background-selected, 10%);
|
||||
}
|
||||
|
||||
.button {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
/* color: #666; */
|
||||
filter: contrast(50%) brightness(40%) grayscale(100%);
|
||||
}
|
||||
|
||||
.disabled-button {
|
||||
color: #666 !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.disabled-button:hover {
|
||||
color: #777 !important;
|
||||
background-color: #222 !important;
|
||||
}
|
||||
|
||||
/* BROWSER-SPECIFIC DISABLE */
|
||||
.disabled-edge {
|
||||
pointer-events: none !important;
|
||||
filter: contrast(50%) brightness(40%) grayscale(100%) !important;
|
||||
content: "NOT SUPPORTED IN THIS BROWSER";
|
||||
}
|
||||
.disabled-edge::after {
|
||||
background-color: #333272;
|
||||
color: #d8d9e6;
|
||||
display: inline-block;
|
||||
font-size: .75em;
|
||||
font-variant: small-caps;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** misc **/
|
||||
|
||||
.warning-color {
|
||||
color: #d6ba4a;
|
||||
}
|
||||
|
||||
.warning, .warning-lite {
|
||||
color: #d6ba4a;
|
||||
padding-left: 35px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.warning::before, .warning-lite::before {
|
||||
content: "⚠ ";
|
||||
display: inline-block;
|
||||
}
|
||||
.warning::before {
|
||||
font-weight: bold;
|
||||
font-size: 2.5em;
|
||||
margin-left: -35px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: $info-color;
|
||||
padding-left: 35px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.info::before {
|
||||
content: "ⓘ";
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
margin-left: -35px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.new {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.new::after {
|
||||
content: "ⓘ";
|
||||
color: $info-color !important;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.experimental::after {
|
||||
// content: "Experimental";
|
||||
content: "Ⓔ";
|
||||
color: #ffde12;
|
||||
// background-color: #1f1f1f;
|
||||
display: inline-block;
|
||||
font-size: .75em;
|
||||
font-variant: small-caps;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
.experimental:hover::after {
|
||||
content: "Ⓔ Experimental";
|
||||
// content: "";
|
||||
color: #ffde12;
|
||||
// background-color: #1f1f1f;
|
||||
display: inline-block;
|
||||
font-size: .75em;
|
||||
font-variant: small-caps;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.sticky-bottom {
|
||||
width: 100%;
|
||||
position: sticky;
|
||||
// position: fixed;
|
||||
bottom: 0px;
|
||||
background-color: rgba($background-primary, 0.7);
|
||||
}
|
||||
|
||||
.rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.ltr {
|
||||
direction: ltr;
|
||||
}
|
||||
.monospace {
|
||||
font-family: 'Overpass Mono';
|
||||
}
|
||||
|
||||
.label {
|
||||
padding-top: 1.5rem;
|
||||
font-size: 1.5rem;
|
||||
font-variant: small-caps;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.label-secondary {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.button-box {
|
||||
padding-top: 0.69rem;
|
||||
padding-bottom: 0.69rem;
|
||||
}
|
||||
|
||||
.indent {
|
||||
padding-left: 4.2rem;
|
||||
}
|
||||
|
||||
.row-padding {
|
||||
padding-top: 0.69rem;
|
||||
padding-bottom: 0.69rem;
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color: $primary-color;
|
||||
}
|
||||
a:hover {
|
||||
color: lighten($primary-color, 10%);
|
||||
}
|
||||
|
||||
/* INPUT FORMATTING */
|
||||
input[type="number"], input[type="text"], input {
|
||||
outline: none;
|
||||
background-color: $input-background;
|
||||
color: $text-normal;
|
||||
padding: 0.1rem;
|
||||
padding-top: 0.2rem;
|
||||
padding-bottom: 0.1rem;
|
||||
margin-left: 1rem;
|
||||
border: 1px solid $input-border;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
background: #444444;
|
||||
color: darken($text-normal, 50%);
|
||||
}
|
||||
|
||||
/* ELEMENT POSITIONING */
|
||||
|
||||
.row {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.float-left {
|
||||
float: left;
|
||||
}
|
||||
.float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* TEXT FORMATTING (no colors) */
|
||||
|
||||
small {
|
||||
font-size: 0.75em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 0.75em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.medium-small {
|
||||
font-size: 0.85em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.smallcaps{
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
.center{
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.invalid-input {
|
||||
border: 1px solid #720 !important;
|
||||
background-color: #410 !important;
|
||||
}
|
||||
|
||||
.button {
|
||||
/*display: inline-block;*/
|
||||
// padding-top: 8px;
|
||||
// padding-bottom: 3px;
|
||||
//padding-left: 5px;
|
||||
//padding-right: 5px;
|
||||
border: 1px solid rgb(39, 39, 39);
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
color: $text-dim;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
user-select: none;;
|
||||
}
|
||||
|
||||
|
||||
.selected, .setting-selected {
|
||||
color: $selected-color !important;
|
||||
background-color: $background-selected !important;
|
||||
}
|
||||
|
||||
.selected-tab {
|
||||
color: $selected-color;
|
||||
}
|
||||
|
||||
.setting-selected {
|
||||
border: 1px solid shade($selected-color, 25%);
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
color: lighten($selected-color, 10%);
|
||||
background-color: lighten($background-selected, 10%);
|
||||
}
|
||||
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
/* color: #666; */
|
||||
filter: contrast(50%) brightness(40%) grayscale(100%);
|
||||
}
|
||||
|
||||
.disabled-button {
|
||||
color: #666 !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.disabled-button:hover {
|
||||
color: #777 !important;
|
||||
background-color: #222 !important;
|
||||
}
|
||||
|
||||
/* BROWSER-SPECIFIC DISABLE */
|
||||
.disabled-edge {
|
||||
pointer-events: none !important;
|
||||
filter: contrast(50%) brightness(40%) grayscale(100%) !important;
|
||||
content: "NOT SUPPORTED IN THIS BROWSER";
|
||||
}
|
||||
.disabled-edge::after {
|
||||
background-color: #333272;
|
||||
color: #d8d9e6;
|
||||
display: inline-block;
|
||||
font-size: .75em;
|
||||
font-variant: small-caps;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** misc **/
|
||||
|
||||
.warning-color {
|
||||
color: #d6ba4a;
|
||||
}
|
||||
|
||||
.warning, .warning-lite {
|
||||
color: #d6ba4a;
|
||||
padding-left: 35px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.warning::before, .warning-lite::before {
|
||||
content: "⚠ ";
|
||||
display: inline-block;
|
||||
}
|
||||
.warning::before {
|
||||
font-weight: bold;
|
||||
font-size: 2.5em;
|
||||
margin-left: -35px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: $info-color;
|
||||
padding-left: 35px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.info::before {
|
||||
content: "ⓘ";
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
margin-left: -35px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.new {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.new::after {
|
||||
content: "ⓘ";
|
||||
color: $info-color !important;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.experimental::after {
|
||||
// content: "Experimental";
|
||||
content: "Ⓔ";
|
||||
color: #ffde12;
|
||||
// background-color: #1f1f1f;
|
||||
display: inline-block;
|
||||
font-size: .75em;
|
||||
font-variant: small-caps;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
.experimental:hover::after {
|
||||
content: "Ⓔ Experimental";
|
||||
// content: "";
|
||||
color: #ffde12;
|
||||
// background-color: #1f1f1f;
|
||||
display: inline-block;
|
||||
font-size: .75em;
|
||||
font-variant: small-caps;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.sticky-bottom {
|
||||
width: 100%;
|
||||
position: sticky;
|
||||
// position: fixed;
|
||||
bottom: 0px;
|
||||
background-color: rgba($background-primary, 0.7);
|
||||
}
|
||||
|
||||
.rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.ltr {
|
||||
direction: ltr;
|
||||
}
|
||||
.monospace {
|
||||
font-family: 'Overpass Mono';
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,53 @@
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.uw-ultrawidify-container-root {
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-auto {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.flex-auto {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.flex-nogrow {
|
||||
flex-grow: 0;
|
||||
}
|
||||
.flex-shrink {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.flex-noshrink {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.flex-nogrow {
|
||||
flex-grow: 0;
|
||||
}
|
||||
.flex-shrink {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.flex-noshrink {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.flex-center {
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.flex-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.flex-cross-center {
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
}
|
||||
.flex-cross-center {
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.flex-self-center {
|
||||
align-self: center;
|
||||
}
|
||||
.flex-self-center {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
.uw-ultrawidify-container-root {
|
||||
/* todo: any form CSS must be nested under this element */
|
||||
}
|
6
src/res/css/uwui-base.scss
Normal file
6
src/res/css/uwui-base.scss
Normal file
@ -0,0 +1,6 @@
|
||||
.uw-ultrawidify-container-root {
|
||||
all: initial;
|
||||
* {
|
||||
all: unset;
|
||||
}
|
||||
}
|
BIN
src/res/icons/ms-promo-tile.png
Normal file
BIN
src/res/icons/ms-promo-tile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
BIN
src/res/icons/uw-microsoft-store-icon.png
Normal file
BIN
src/res/icons/uw-microsoft-store-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
@ -23,10 +23,18 @@ const config = {
|
||||
filename: '[name].js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue'],
|
||||
// maybe we'll move to TS some day, but today is not the day
|
||||
extensions: [
|
||||
// '.ts', '.tsx',
|
||||
'.js', '.vue'
|
||||
],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
// {
|
||||
// test: /\.tsx?$/,
|
||||
// loader: 'ts-loader',
|
||||
// },
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loaders: 'vue-loader',
|
||||
@ -77,6 +85,16 @@ const config = {
|
||||
new CopyWebpackPlugin([
|
||||
{ from: 'res', to: 'res', ignore: ['css', 'css/**']},
|
||||
{ from: 'ext', to: 'ext', ignore: ['conf/*', 'lib/**']},
|
||||
|
||||
// we need to get webextension-polyfill and put it in common/lib
|
||||
{ from: '../node_modules/webextension-polyfill/dist/browser-polyfill.js', to: 'common/lib/browser-polyfill.js'},
|
||||
|
||||
// This is a hack to get bootstrap icons svg file in /res/icons
|
||||
{ from: '../node_modules/bootstrap-icons/bootstrap-icons.svg', to: 'res/icons/bootstrap-icons.svg'},
|
||||
|
||||
// This is extension icon, as used on extension lists and/or extension's action button
|
||||
// This folder does not contain any GUI icons — these are in /res/icons.
|
||||
// (TODO: check if this copy is even necessary — /icons has same content as /res/icons)
|
||||
{ from: 'icons', to: 'icons', ignore: ['icon.xcf'] },
|
||||
{ from: 'popup/popup.html', to: 'popup/popup.html', transform: transformHtml },
|
||||
{ from: 'options/options.html', to: 'options/options.html', transform: transformHtml },
|
||||
|
Loading…
Reference in New Issue
Block a user