Merge tag '4.5.0' into stable

This commit is contained in:
Tamius Han 2020-12-17 01:50:40 +01:00
commit 2f802f4e68
50 changed files with 9994 additions and 1408 deletions

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

@ -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>

File diff suppressed because one or more lines are too long

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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>

View 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;

View File

@ -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;

View File

@ -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>&nbsp; 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
View 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>&nbsp;
<template
v-for="action of hideActions"
:key="action"
>
<i @click="closeNotification">
<a
class="hide-action-button"
@click="action.command"
>
{{action.label}}
</a>
<wbr>&nbsp;
</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>

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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: "",
}
}
}
}
}
];

View File

@ -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: "",
}
}
},
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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");
}

View File

@ -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()) {

View File

@ -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;

View File

@ -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;

View 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
View 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;

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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;`);

View File

@ -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)

View File

@ -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");
}
}

View File

@ -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",

View File

@ -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) {

View File

@ -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\

View File

@ -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',

View File

@ -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>

View File

@ -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');

View File

@ -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>

View File

@ -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,

View File

@ -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 '';

View File

@ -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 &amp; via email</a>)
(On options page, section 'Action &amp; 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>

View File

@ -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>

View File

@ -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');

View File

@ -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';
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,3 @@
.uw-ultrawidify-container-root {
/* todo: any form CSS must be nested under this element */
}

View File

@ -0,0 +1,6 @@
.uw-ultrawidify-container-root {
all: initial;
* {
all: unset;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -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 },