Compare commits

..

No commits in common. "ca751a660fb66181f4de07fa07210b5a29e18f5c" and "877fc96da10d9b105dd0a7e5ac13b0b8e427a5f7" have entirely different histories.

62 changed files with 970 additions and 1749 deletions

19
.vscode/settings.json vendored
View File

@ -10,8 +10,6 @@
"blackframe", "blackframe",
"canvas", "canvas",
"clickthrough", "clickthrough",
"cmpa",
"cmpb",
"com", "com",
"comms", "comms",
"csui", "csui",
@ -25,37 +23,26 @@
"fith", "fith",
"fitw", "fitw",
"fuckup", "fuckup",
"fucky",
"geoblocked",
"geoblocking",
"gfycat", "gfycat",
"gmail", "gmail",
"guardline", "guardline",
"han", "han",
"haram", "haram",
"Heebo",
"hoverable",
"iframe", "iframe",
"imgur", "imgur",
"insta", "insta",
"jsoneditor",
"jwplayer",
"kavin",
"letterboxed", "letterboxed",
"manjaro", "manjaro",
"mdicon", "mdicon",
"mdijs", "mdijs",
"metaivi",
"minification", "minification",
"mitigations", "mitigations",
"mstefan",
"nogrow", "nogrow",
"noshrink", "noshrink",
"outro", "outro",
"PILLARBOX", "PILLARBOX",
"PILLARBOXED", "PILLARBOXED",
"polyfill", "polyfill",
"problemo",
"recursing", "recursing",
"reddit", "reddit",
"rescan", "rescan",
@ -64,7 +51,6 @@
"scrollbar", "scrollbar",
"shitiness", "shitiness",
"smallcaps", "smallcaps",
"spaghettified",
"suboption", "suboption",
"subscan", "subscan",
"tabitem", "tabitem",
@ -75,7 +61,6 @@
"ultrawidify", "ultrawidify",
"unmark", "unmark",
"unmarking", "unmarking",
"unsets",
"unshift", "unshift",
"uwid", "uwid",
"uwui", "uwui",
@ -83,12 +68,8 @@
"vids", "vids",
"vuejs", "vuejs",
"vuex", "vuex",
"wakanim",
"webextension", "webextension",
"webextensions", "webextensions",
"wuckies",
"wucky",
"ycenter",
"youtube" "youtube"
], ],
"cSpell.ignoreWords": [ "cSpell.ignoreWords": [

14
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "ultrawidify", "name": "ultrawidify",
"version": "6.3.994", "version": "6.3.993",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ultrawidify", "name": "ultrawidify",
"version": "6.3.994", "version": "6.3.993",
"dependencies": { "dependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-properties": "^7.18.6",
"@mdi/font": "^7.4.47", "@mdi/font": "^7.4.47",
@ -16,7 +16,6 @@
"fs-extra": "^11.3.2", "fs-extra": "^11.3.2",
"gl-matrix": "^3.4.4", "gl-matrix": "^3.4.4",
"json-cyclic": "1.0.2", "json-cyclic": "1.0.2",
"json-difference": "^1.16.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mdi-vue": "^3.0.13", "mdi-vue": "^3.0.13",
"typescript": "^5.9.3", "typescript": "^5.9.3",
@ -13497,15 +13496,6 @@
"integrity": "sha512-K0Y9wQnrc13Ef+P8KY8SjFyVAz941CTrTVmu3oyQPExhaFFYXXLXnEuRi1tqYOS1B8WtFIxIwA7nHRJ64Uvf5Q==", "integrity": "sha512-K0Y9wQnrc13Ef+P8KY8SjFyVAz941CTrTVmu3oyQPExhaFFYXXLXnEuRi1tqYOS1B8WtFIxIwA7nHRJ64Uvf5Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/json-difference": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/json-difference/-/json-difference-1.16.1.tgz",
"integrity": "sha512-tNVUzBKn2I4BB4Cjn+j+Kq/BfLNwqtEvxhnlyuX5XJmKn9VlfV5MvaTbB/ure/toFgLaD6XglOUt+dd4iMQ3dA==",
"license": "MIT",
"engines": {
"node": ">=18.17.x"
}
},
"node_modules/json-parse-better-errors": { "node_modules/json-parse-better-errors": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",

View File

@ -1,6 +1,6 @@
{ {
"name": "ultrawidify", "name": "ultrawidify",
"version": "6.3.994", "version": "6.3.993",
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.", "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>", "author": "Tamius Han <tamius.han@gmail.com>",
"scripts": { "scripts": {
@ -33,7 +33,6 @@
"fs-extra": "^11.3.2", "fs-extra": "^11.3.2",
"gl-matrix": "^3.4.4", "gl-matrix": "^3.4.4",
"json-cyclic": "1.0.2", "json-cyclic": "1.0.2",
"json-difference": "^1.16.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mdi-vue": "^3.0.13", "mdi-vue": "^3.0.13",
"typescript": "^5.9.3", "typescript": "^5.9.3",

View File

@ -1,11 +0,0 @@
const path = require('path')
module.exports = {
'@src': path.resolve(__dirname, 'src'),
'@csui': path.resolve(__dirname, 'src/csui'),
'@ui': path.resolve(__dirname, 'src/ui'),
'@components': path.resolve(__dirname, 'src/ui/components'),
'@res': path.resolve(__dirname, 'src/ui/res'),
'@': path.resolve(__dirname, 'src'),
};

View File

@ -1,22 +1,5 @@
const path = require('path');
const aliases = require('./paths.config');
module.exports = { module.exports = {
plugins: [ plugins: [
require('postcss-import')({
resolve(id) {
for (const alias in aliases) {
if (id === alias || id.startsWith(alias + '/')) {
return path.resolve(
aliases[alias],
id.slice(alias.length + 1)
)
}
}
return id
},
}),
require('@tailwindcss/postcss'), require('@tailwindcss/postcss'),
require('postcss-nested'), require('postcss-nested'),
require('autoprefixer'), require('autoprefixer'),

View File

@ -1,9 +1,10 @@
enum ExtensionMode { enum ExtensionMode {
AutoDisabled = -2,
Disabled = -1, Disabled = -1,
Default = 0, Default = 0,
FullScreen = 1, Whitelist = 1,
Theater = 2, Basic = 2,
All = 3 Enabled = 3,
} };
export default ExtensionMode; export default ExtensionMode;

View File

@ -1,4 +1,4 @@
enum LegacyExtensionMode { enum ExtensionMode {
AutoDisabled = -2, AutoDisabled = -2,
Disabled = -1, Disabled = -1,
Default = 0, Default = 0,
@ -7,4 +7,4 @@ enum LegacyExtensionMode {
Enabled = 3, Enabled = 3,
}; };
export default LegacyExtensionMode; export default ExtensionMode;

View File

@ -1,9 +0,0 @@
export enum SiteSupportLevel {
OfficialSupport = 'official',
CommunitySupport = 'community',
UserDefined = 'user-defined',
BetaSupport = 'testing',
OfficialBlacklist = 'officially-disabled',
Unknown = 'unknown',
UserModified = 'modified',
}

View File

@ -6,14 +6,13 @@ import AspectRatioType from '../enums/AspectRatioType.enum'
import CropModePersistence from '../enums/CropModePersistence.enum' import CropModePersistence from '../enums/CropModePersistence.enum'
import EmbeddedContentSettingsOverridePolicy from '../enums/EmbeddedContentSettingsOverridePolicy.enum' import EmbeddedContentSettingsOverridePolicy from '../enums/EmbeddedContentSettingsOverridePolicy.enum'
import ExtensionMode from '../enums/ExtensionMode.enum' import ExtensionMode from '../enums/ExtensionMode.enum'
import { SiteSupportLevel } from '../enums/SiteSupportLevel.enum'
import StretchType from '../enums/StretchType.enum' import StretchType from '../enums/StretchType.enum'
import VideoAlignmentType from '../enums/VideoAlignmentType.enum' import VideoAlignmentType from '../enums/VideoAlignmentType.enum'
export enum ExtensionEnvironment { export enum ExtensionEnvironment {
Normal = ExtensionMode.All, Normal = 'normal',
Theater = ExtensionMode.Theater, Theater = 'theater',
Fullscreen = ExtensionMode.FullScreen, Fullscreen = 'fullscreen',
} }
export interface DevUiConfig { export interface DevUiConfig {
@ -49,6 +48,12 @@ interface RestrictionsSettings {
onlyAllowAutodetectionInFullScreen?: boolean; // if enabled, autodetection will only start once in full screen onlyAllowAutodetectionInFullScreen?: boolean; // if enabled, autodetection will only start once in full screen
} }
interface ExtensionEnvironmentSettingsInterface {
normal: ExtensionMode,
theater: ExtensionMode,
fullscreen: ExtensionMode,
}
export interface CommandInterface { export interface CommandInterface {
action: string, action: string,
label: string, label: string,
@ -320,6 +325,8 @@ interface SettingsInterface {
ui: { ui: {
inPlayer: { inPlayer: {
enabled: boolean, // Deprecated — moved to site settings
enabledFullscreenOnly: boolean, // Deprecated — moved to site settings
popupAlignment: 'left' | 'right', popupAlignment: 'left' | 'right',
minEnabledWidth: number, // don't show UI if player is narrower than % of screen width minEnabledWidth: number, // don't show UI if player is narrower than % of screen width
minEnabledHeight: number, // don't show UI if player is narrower than % of screen height minEnabledHeight: number, // don't show UI if player is narrower than % of screen height
@ -438,18 +445,18 @@ interface SettingsInterface {
} }
export interface SiteSettingsInterface { export interface SiteSettingsInterface {
enable: ExtensionMode; enable: ExtensionEnvironmentSettingsInterface;
enableAard: ExtensionMode; enableAard: ExtensionEnvironmentSettingsInterface;
enableKeyboard: ExtensionMode; enableKeyboard: ExtensionEnvironmentSettingsInterface;
enableUI: ExtensionMode; enableUI: ExtensionEnvironmentSettingsInterface; // Lies! enableUI doesn't use 'theater' property (but uses the other two)
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy; // presumed to be 'Always' if not defined applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy; // presumed to be 'Always' if not defined
overrideWhenEmbedded?: boolean; overrideWhenEmbedded?: boolean;
autocreated?: boolean; autocreated?: boolean;
type?: SiteSupportLevel; type?: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
defaultType: SiteSupportLevel; defaultType: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
// must be defined in @global and @empty // must be defined in @global and @empty
persistCSA?: CropModePersistence, // CSA - crop, stretch, alignment persistCSA?: CropModePersistence, // CSA - crop, stretch, alignment

View File

@ -1,40 +1,42 @@
<template> <template>
<div> <div>
<!-- Enable extension --> <!-- Enable extension -->
<pre>{{simpleExtensionSettings}}</pre>
<div class="field"> <div class="field">
<div class="label"> <div class="label">
Enable <span class="color-emphasis">extension</span> Enable <span class="color-emphasis">extension</span>
<span class="sub-label"><br/>under the following conditions:</span> <span class="sub-label"><br/>under the following conditions:</span>
</div> </div>
<pre>{{simpleExtensionSettings}}</pre>
<div class="select"> <div class="select">
<select <select
:value="simpleExtensionSettings.enable" :value="simpleExtensionSettings.enable"
@click="setExtensionMode('enable', $event)" @click="setExtensionMode('enable', $event)"
> >
<option
v-if="simpleExtensionSettings.enable === 'complex'"
value="complex"
>
(Site uses advanced settings)
</option>
<template v-if="isDefaultConfiguration"> <template v-if="isDefaultConfiguration">
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Disabled by default Disabled by default
</option> </option>
</template> </template>
<template v-else> <template v-else>
<option :value="ExtensionMode.Default"> <option value="default">
Use default ({{simpleDefaultSettings.enableAard}}) Use default ({{simpleDefaultSettings.enable}})
</option> </option>
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Never Never
</option> </option>
</template> </template>
<option :value="ExtensionMode.FullScreen"> <option value="fs">
Fullscreen only Fullscreen only
</option> </option>
<option :value="ExtensionMode.Theater"> <option value="theater">
Fullscreen and theater mode Fullscreen and theater mode
</option> </option>
<option :value="ExtensionMode.All"> <option value="enabled">
Always Always
</option> </option>
</select> </select>
@ -54,26 +56,32 @@
:value="simpleExtensionSettings.enableAard" :value="simpleExtensionSettings.enableAard"
@click="setExtensionMode('enableAard', $event)" @click="setExtensionMode('enableAard', $event)"
> >
<option
v-if="simpleExtensionSettings.enable === 'complex'"
value="complex"
>
(Site uses advanced settings)
</option>
<template v-if="isDefaultConfiguration"> <template v-if="isDefaultConfiguration">
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Disabled by default Disabled by default
</option> </option>
</template> </template>
<template v-else> <template v-else>
<option :value="ExtensionMode.Default"> <option value="default">
Use default ({{simpleDefaultSettings.enableAard}}) Use default ({{simpleDefaultSettings.enableAard}})
</option> </option>
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Never Never
</option> </option>
</template> </template>
<option :value="ExtensionMode.FullScreen"> <option value="fs">
Fullscreen only Fullscreen only
</option> </option>
<option :value="ExtensionMode.Theater"> <option value="theater">
Fullscreen and theater mode Fullscreen and theater mode
</option> </option>
<option :value="ExtensionMode.All"> <option value="enabled">
Always Always
</option> </option>
</select> </select>
@ -91,26 +99,32 @@
:value="simpleExtensionSettings.enableKeyboard" :value="simpleExtensionSettings.enableKeyboard"
@click="setExtensionMode('enableKeyboard', $event)" @click="setExtensionMode('enableKeyboard', $event)"
> >
<option
v-if="simpleExtensionSettings.enable === 'complex'"
value="complex"
>
(Site uses advanced settings)
</option>
<template v-if="isDefaultConfiguration"> <template v-if="isDefaultConfiguration">
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Disabled by default Disabled by default
</option> </option>
</template> </template>
<template v-else> <template v-else>
<option :value="ExtensionMode.Default"> <option value="default">
Use default ({{simpleDefaultSettings.enableAard}}) Use default ({{simpleDefaultSettings.enableKeyboard}})
</option> </option>
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Never Never
</option> </option>
</template> </template>
<option :value="ExtensionMode.FullScreen"> <option value="fs">
Fullscreen only Fullscreen only
</option> </option>
<option :value="ExtensionMode.Theater"> <option value="theater">
Fullscreen and theater mode Fullscreen and theater mode
</option> </option>
<option :value="ExtensionMode.All"> <option value="enabled">
Always Always
</option> </option>
</select> </select>
@ -129,26 +143,23 @@
@click="setExtensionMode('enableUI', $event)" @click="setExtensionMode('enableUI', $event)"
> >
<template v-if="isDefaultConfiguration"> <template v-if="isDefaultConfiguration">
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Disabled by default Disabled by default
</option> </option>
</template> </template>
<template v-else> <template v-else>
<option :value="ExtensionMode.Default"> <option value="default">
Use default ({{simpleDefaultSettings.enableAard}}) Use default ({{simpleDefaultSettings.enableUI}})
</option> </option>
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Never Never
</option> </option>
</template> </template>
<option :value="ExtensionMode.FullScreen"> <option value="fs">
Fullscreen only Fullscreen only
</option> </option>
<option :value="ExtensionMode.Theater"> <option value="theater">
Fullscreen and theater mode Always where possible
</option>
<option :value="ExtensionMode.All">
Always
</option> </option>
</select> </select>
</div> </div>
@ -285,7 +296,7 @@
v-if="!isDefaultConfiguration" v-if="!isDefaultConfiguration"
:value="CropModePersistence.Default" :value="CropModePersistence.Default"
> >
Use default ({{defaultPersistenceLabel}}) Use default ({{defaultPersistanceLabel}})
</option> </option>
<option :value="CropModePersistence.Disabled">Disabled</option> <option :value="CropModePersistence.Disabled">Disabled</option>
<option :value="CropModePersistence.UntilPageReload">Until page reload</option> <option :value="CropModePersistence.UntilPageReload">Until page reload</option>
@ -377,7 +388,7 @@ export default {
siteDefaultCropPersistence() { siteDefaultCropPersistence() {
return this.siteSettings.raw?.persistCSA ?? undefined; return this.siteSettings.raw?.persistCSA ?? undefined;
}, },
defaultPersistenceLabel() { defaultPersistanceLabel() {
switch (this.siteSettings.defaultSettings.persistCSA) { switch (this.siteSettings.defaultSettings.persistCSA) {
case CropModePersistence.CurrentSession: case CropModePersistence.CurrentSession:
return 'current session'; return 'current session';
@ -414,12 +425,64 @@ export default {
break; break;
} }
return settingsData[component];
try {
if (
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Disabled
&& settingsData?.[component]?.fullscreen === ExtensionMode.Disabled
) {
return 'disabled';
}
if (
( settingsData?.[component]?.normal === ExtensionMode.Default || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Default
&& settingsData?.[component]?.fullscreen === ExtensionMode.Default
) {
// console.log(
// component, 'is set to default because:\n',
// `\nsettingsData[${component}].normal: ${settingsData?.[component]?.normal} || component is enableUI?`, component,
// `\nsettingsData[${component}].theater: ${settingsData?.[component]?.normal}`,
// `\nsettingsData[${component}].fullscreen: ${settingsData?.[component]?.normal}`,
// `\n\n(expected values:`, ExtensionMode.Default
// )
return 'default';
}
if (
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Disabled
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
) {
return 'fs';
}
if (
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Enabled
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
) {
return 'theater';
}
if (
( settingsData?.[component]?.normal === ExtensionMode.Enabled || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Enabled
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
) {
return 'enabled';
}
return 'complex';
} catch (e) {
return 'loading';
}
}, },
getDefaultOptionLabel(component) { getDefaultOptionLabel(component) {
const componentValue = this.compileSimpleSettings(component, 'default'); const componentValue = this.compileSimpleSettings(component, 'default');
if (componentValue === 'loading') {
return componentValue;
}
if (component === 'enableUI') { if (component === 'enableUI') {
switch (componentValue) { switch (componentValue) {
case 'fs': case 'fs':
@ -517,7 +580,7 @@ export default {
this._computedWatchers?.siteDefaultStretch?.run(); this._computedWatchers?.siteDefaultStretch?.run();
this._computedWatchers?.siteDefaultAlignment?.run(); this._computedWatchers?.siteDefaultAlignment?.run();
this._computedWatchers?.siteDefaultCropPersistence?.run(); this._computedWatchers?.siteDefaultCropPersistence?.run();
this._computedWatchers?.defaultPersistenceLabel?.run(); this._computedWatchers?.defaultPersistanceLabel?.run();
this.$nextTick( () => this.$forceUpdate()); this.$nextTick( () => this.$forceUpdate());
}, },

View File

@ -274,7 +274,7 @@ export default {
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1}); this.eventBus?.sendToTunnel('set-zoom', {zoom: 1});
}, },
changeZoom(newZoom, axis) { changeZoom(newZoom, axis) {
// we store zoom logarithmically on this component // we store zoom logarithmically on this compnent
if (!axis) { if (!axis) {
this.zoom.x = newZoom; this.zoom.x = newZoom;
} else { } else {

View File

@ -110,7 +110,7 @@ export default class UWContent {
this.pageInfo = new PageInfo(this.eventBus, this.siteSettings, this.settings, this.logAggregator); this.pageInfo = new PageInfo(this.eventBus, this.siteSettings, this.settings, this.logAggregator);
this.logger.debug('setup', "pageInfo initialized."); this.logger.debug('setup', "pageInfo initialized.");
this.logger.debug('setup', "will try to initiate KeyboardHandler."); this.logger.debug('setup', "will try to initate KeyboardHandler.");
if (this.keyboardHandler) { if (this.keyboardHandler) {
this.keyboardHandler.destroy(); this.keyboardHandler.destroy();

View File

@ -1,44 +1,41 @@
// How to use: // How to use:
// version: {ExtensionConf object, but only properties that get overwritten} // version: {ExtensionConf object, but only properties that get overwritten}
import StretchType from '../../common/enums/StretchType.enum'; import StretchType from '../../common/enums/StretchType.enum';
// import ExtensionMode from '../../common/enums/ExtensionMode.enum'; import ExtensionMode from '../../common/enums/ExtensionMode.enum';
import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum'; import VideoAlignmentType from '../../common/enums/VideoAlignmentType.enum';
import BrowserDetect from './BrowserDetect'; import BrowserDetect from './BrowserDetect';
import SettingsInterface, { SiteSettingsInterface } from '../../common/interfaces/SettingsInterface'; import SettingsInterface from '../../common/interfaces/SettingsInterface';
import { _cp } from '../../common/js/utils'; import { _cp } from '../../common/js/utils';
import CropModePersistence from '../../common/enums/CropModePersistence.enum'; import CropModePersistence from '../../common/enums/CropModePersistence.enum';
import AspectRatioType from '../../common/enums/AspectRatioType.enum'; import AspectRatioType from '../../common/enums/AspectRatioType.enum';
import { update } from 'lodash'; import { update } from 'lodash';
import EmbeddedContentSettingsOverridePolicy from '../../common/enums/EmbeddedContentSettingsOverridePolicy.enum'; import EmbeddedContentSettingsOverridePolicy from '../../common/enums/EmbeddedContentSettingsOverridePolicy.enum';
import LegacyExtensionMode from '../../common/enums/LegacyExtensionMode.enum';
import ExtensionMode from '../../common/enums/ExtensionMode.enum';
const ExtensionConfPatch = Object.freeze([ const ExtensionConfPatch = Object.freeze([
{ {
forVersion: '6.2.4', forVersion: '6.2.4',
updateFn: (userOptions: any, defaultOptions, logger?) => { updateFn: (userOptions: SettingsInterface, defaultOptions) => {
for (const site in userOptions.sites) { for (const site in userOptions.sites) {
(userOptions as any).sites[site].enableUI = { userOptions.sites[site].enableUI = {
fullscreen: LegacyExtensionMode.Default, fullscreen: ExtensionMode.Default,
theater: LegacyExtensionMode.Default, theater: ExtensionMode.Default,
normal: LegacyExtensionMode.Default, normal: ExtensionMode.Default,
} }
} }
userOptions.sites['@global'].enableUI = { userOptions.sites['@global'].enableUI = {
fullscreen: userOptions.ui.inPlayer.enabled ? LegacyExtensionMode.Enabled : LegacyExtensionMode.Disabled, fullscreen: userOptions.ui.inPlayer.enabled ? ExtensionMode.Enabled : ExtensionMode.Disabled,
theater: LegacyExtensionMode.Enabled, theater: ExtensionMode.Enabled,
normal: (userOptions.ui.inPlayer.enabled && !userOptions.ui.inPlayer.enabledFullscreenOnly) ? LegacyExtensionMode.Enabled : LegacyExtensionMode.Disabled normal: (userOptions.ui.inPlayer.enabled && !userOptions.ui.inPlayer.enabledFullscreenOnly) ? ExtensionMode.Enabled : ExtensionMode.Disabled
} }
userOptions.sites['@empty'].enableUI = { userOptions.sites['@empty'].enableUI = {
fullscreen: LegacyExtensionMode.Default, fullscreen: ExtensionMode.Default,
theater: LegacyExtensionMode.Default, theater: ExtensionMode.Default,
normal: LegacyExtensionMode.Default, normal: ExtensionMode.Default,
} }
} }
}, { }, {
forVersion: '6.2.6', forVersion: '6.2.6',
updateFn: (userOptions: any, defaultOptions, logger?) => { updateFn: (userOptions: SettingsInterface, defaultOptions) => {
console.log('[ultrawidify] Migrating settings — applying patches for version 6.2.6'); console.log('[ultrawidify] Migrating settings — applying patches for version 6.2.6');
if (!userOptions.commands) { if (!userOptions.commands) {
@ -244,7 +241,7 @@ const ExtensionConfPatch = Object.freeze([
} }
}, { }, {
forVersion: '6.3.92', forVersion: '6.3.92',
updateFn: (userOptions: any) => { updateFn: (userOptions: SettingsInterface) => {
// applyToEmbeddedContent is now an enum, and also no longer optional // applyToEmbeddedContent is now an enum, and also no longer optional
for (const site in userOptions.sites) { for (const site in userOptions.sites) {
if (userOptions.sites[site].applyToEmbeddedContent === undefined) { if (userOptions.sites[site].applyToEmbeddedContent === undefined) {
@ -273,41 +270,11 @@ const ExtensionConfPatch = Object.freeze([
}, },
{ {
forVersion: '6.3.98', forVersion: '6.3.98',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => { upgradeFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
userOptions.aard = defaultOptions.aard; userOptions.aard = defaultOptions.aard;
userOptions.aardLegacy = defaultOptions.aardLegacy; userOptions.aardLegacy = defaultOptions.aardLegacy;
delete (userOptions as any).arDetect; delete (userOptions as any).arDetect;
} }
},
{
forVersion: '6.3.994',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface, logger?) => {
const convertLegacyExtensionMode = (option: {normal: LegacyExtensionMode, theater: LegacyExtensionMode, fullscreen: LegacyExtensionMode}) => {
logger.log('updateFn', 'converting option', option);
if (option.normal === LegacyExtensionMode.Enabled) {
return ExtensionMode.All;
}
if (option.theater === LegacyExtensionMode.Enabled) {
return ExtensionMode.Theater;
}
if (option.fullscreen === LegacyExtensionMode.Enabled) {
return ExtensionMode.FullScreen;
}
if (option.fullscreen === LegacyExtensionMode.Disabled) {
return ExtensionMode.Disabled;
}
return ExtensionMode.Default;
}
for (const key in userOptions.sites) {
userOptions.sites[key].enable = convertLegacyExtensionMode(userOptions.sites[key].enable as any);
userOptions.sites[key].enableAard = convertLegacyExtensionMode(userOptions.sites[key].enable as any);
userOptions.sites[key].enableKeyboard = convertLegacyExtensionMode(userOptions.sites[key].enable as any);
userOptions.sites[key].enableUI = convertLegacyExtensionMode(userOptions.sites[key].enable as any);
logger.log('migrated site', key, userOptions.sites[key]);
}
}
} }
]); ]);

View File

@ -12,7 +12,6 @@ import { Extension } from 'typescript';
import EmbeddedContentSettingsOverridePolicy from '../../common/enums/EmbeddedContentSettingsOverridePolicy.enum'; import EmbeddedContentSettingsOverridePolicy from '../../common/enums/EmbeddedContentSettingsOverridePolicy.enum';
import { AardPollingOptions } from '../lib/aard/enums/aard-polling-options.enum'; import { AardPollingOptions } from '../lib/aard/enums/aard-polling-options.enum';
import { AardSubtitleCropMode } from '../lib/aard/enums/aard-subtitle-crop-mode.enum'; import { AardSubtitleCropMode } from '../lib/aard/enums/aard-subtitle-crop-mode.enum';
import { SiteSupportLevel } from '../../common/enums/SiteSupportLevel.enum';
if(Debug.debug) if(Debug.debug)
console.log("Loading: ExtensionConf.js"); console.log("Loading: ExtensionConf.js");
@ -263,6 +262,8 @@ const ExtensionConf: SettingsInterface = {
ui: { ui: {
inPlayer: { inPlayer: {
enabled: true, // enable by default on new installs
enabledFullscreenOnly: false,
minEnabledWidth: 0.75, minEnabledWidth: 0.75,
minEnabledHeight: 0.75, minEnabledHeight: 0.75,
activation: 'player', activation: 'player',
@ -756,6 +757,65 @@ const ExtensionConf: SettingsInterface = {
arguments: { arguments: {
arPersistence: CropModePersistence.Default arPersistence: CropModePersistence.Default
} }
}, {
action: 'set-extension-enabled',
label: 'Enable extension',
arguments: {
mode: ExtensionMode.Enabled,
},
internalOnly: true,
}, {
action: 'set-extension-enabled',
label: 'Whitelist',
arguments: {
mode: ExtensionMode.Whitelist,
},
internalOnly: true,
}, {
action: 'set-extension-enabled',
label: 'Disable',
arguments: {
mode: ExtensionMode.Disabled
},
internalOnly: true,
}, {
action: 'set-extension-enabled',
label: 'Use default option',
arguments: {
mode: ExtensionMode.Default
},
internalOnly: true
}, {
action: 'set-autoar-enabled',
label: 'Enabled',
comment: 'Enable automatic aspect ratio detection if possible',
arguments: {
mode: ExtensionMode.Enabled
},
internalOnly: true
}, {
action: 'set-autoar-enabled',
label: 'Whitelist',
comment: 'Enable automatic aspect ratio detection if possible, but only on whitelisted sites',
arguments: {
mode: ExtensionMode.Whitelist,
},
internalOnly: true,
}, {
action: 'set-autoar-enabled',
label: 'Disable',
comment: 'Disable aspect ratio detection',
arguments: {
mode: ExtensionMode.Disabled
},
internalOnly: true,
}, {
action: 'set-autoar-enabled',
label: 'Use default option',
arguments: {
mode: ExtensionMode.Default
},
internalOnly: true
}] }]
}, },
mitigations: { mitigations: {
@ -802,14 +862,29 @@ const ExtensionConf: SettingsInterface = {
// to avoid writing this multiple times. Tags: // to avoid writing this multiple times. Tags:
// #g — only available in @global // #g — only available in @global
// #s — only available for specific site // #s — only available for specific site
enable: ExtensionMode.Disabled, enable: { // How should extension work:
enableAard: ExtensionMode.Disabled, fullscreen: ExtensionMode.Disabled, // 'enabled' - work everywhere except blacklist
enableKeyboard: ExtensionMode.Disabled, theater: ExtensionMode.Disabled, //
enableUI: ExtensionMode.FullScreen, normal: ExtensionMode.Disabled, // 'disabled' - work nowhere
}, // 'default' - follow global rules (#s)
enableAard: { // Should we try to automatically detect aspect ratio?
fullscreen: ExtensionMode.Disabled, // Options: 'enabled', 'default' (#s), 'disabled'
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled
},
enableUI: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled, // Lies! means 'theater-ish'
normal: ExtensionMode.Disabled // Not actually used.
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
defaultType: SiteSupportLevel.Unknown, defaultType: 'unknown',
persistCSA: CropModePersistence.Disabled, persistCSA: CropModePersistence.Disabled,
defaults: { defaults: {
@ -819,15 +894,30 @@ const ExtensionConf: SettingsInterface = {
} }
}, },
"@empty": { // placeholder settings object with fallbacks to @global "@empty": { // placeholder settings object with fallbacks to @global
enable: ExtensionMode.Default, enable: {
enableAard: ExtensionMode.Default, fullscreen: ExtensionMode.Default,
enableKeyboard: ExtensionMode.Default, theater: ExtensionMode.Default,
enableUI: ExtensionMode.Default, normal: ExtensionMode.Default,
},
enableAard: {
fullscreen: ExtensionMode.Default,
theater: ExtensionMode.Default,
normal: ExtensionMode.Default,
},
enableKeyboard: {
fullscreen: ExtensionMode.Default,
theater: ExtensionMode.Default,
normal: ExtensionMode.Default
},
enableUI: {
fullscreen: ExtensionMode.Default,
theater: ExtensionMode.Default,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
type: SiteSupportLevel.UserDefined, type: 'user-defined',
defaultType: SiteSupportLevel.UserDefined, defaultType: 'user-defined',
persistCSA: CropModePersistence.Default, persistCSA: CropModePersistence.Default,
defaults: { defaults: {
crop: null, crop: null,
@ -836,22 +926,37 @@ const ExtensionConf: SettingsInterface = {
} }
}, },
"www.youtube.com" : { "www.youtube.com" : {
enable: ExtensionMode.All, enable: {
enableAard: ExtensionMode.All, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.All, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.All, normal: ExtensionMode.Enabled,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
overrideWhenEmbedded: true, overrideWhenEmbedded: true,
override: false, // ignore value localStorage in favour of this override: false, // ignore value localStorage in favour of this
type: SiteSupportLevel.OfficialSupport, // is officially supported? (Alternatives are 'community' and 'user-defined') type: 'official', // is officially supported? (Alternatives are 'community' and 'user-defined')
defaultType: SiteSupportLevel.OfficialSupport, // if user mucks around with settings, type changes to 'user-defined'. defaultType: 'official', // if user mucks around with settings, type changes to 'user-defined'.
// We still want to know what the original type was, hence defaultType // We still want to know what the original type was, hence defaultType
activeDOMConfig: 'official', activeDOMConfig: 'official',
DOMConfig: { DOMConfig: {
'official': { 'official': {
type: SiteSupportLevel.OfficialSupport, type: 'official',
elements: { elements: {
player: { player: {
manual: true, manual: true,
@ -862,23 +967,38 @@ const ExtensionConf: SettingsInterface = {
} }
}, },
"www.youtube-nocookie.com": { "www.youtube-nocookie.com": {
enable: ExtensionMode.All, enable: {
enableAard: ExtensionMode.All, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.All, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.FullScreen, normal: ExtensionMode.Enabled,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
overrideWhenEmbedded: true, overrideWhenEmbedded: true,
override: false, // ignore value localStorage in favour of this override: false, // ignore value localStorage in favour of this
type: SiteSupportLevel.OfficialSupport, // is officially supported? (Alternatives are 'community' and 'user-defined') type: 'official', // is officially supported? (Alternatives are 'community' and 'user-defined')
defaultType: SiteSupportLevel.OfficialSupport, // if user mucks around with settings, type changes to 'user-defined'. defaultType: 'official', // if user mucks around with settings, type changes to 'user-defined'.
// We still want to know what the original type was, hence defaultType // We still want to know what the original type was, hence defaultType
activeDOMConfig: 'official', activeDOMConfig: 'official',
DOMConfig: { DOMConfig: {
'official': { 'official': {
type: SiteSupportLevel.OfficialSupport, type: 'official',
elements: { elements: {
player: { player: {
manual: true, manual: true,
@ -889,28 +1009,59 @@ const ExtensionConf: SettingsInterface = {
} }
}, },
"www.netflix.com" : { "www.netflix.com" : {
enable: ExtensionMode.Theater, enable: {
enableAard: ExtensionMode.Disabled, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.All, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.All, normal: ExtensionMode.Default,
},
enableAard: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
override: false, override: false,
type: SiteSupportLevel.CommunitySupport, type: 'community',
defaultType: SiteSupportLevel.CommunitySupport, defaultType: 'community',
}, },
"www.disneyplus.com" : { "www.disneyplus.com" : {
enable: ExtensionMode.Theater, enable: {
enableAard: ExtensionMode.Theater, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.All, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.FullScreen, normal: ExtensionMode.Default,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Default,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Default
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Default
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
type: SiteSupportLevel.CommunitySupport, type: 'community',
defaultType: SiteSupportLevel.CommunitySupport, defaultType: 'community',
activeDOMConfig: 'community-mstefan99', activeDOMConfig: 'community-mstefan99',
DOMConfig: { DOMConfig: {
'community-mstefan99': { 'community-mstefan99': {
type: SiteSupportLevel.OfficialSupport, type: 'official',
elements: { elements: {
player: { player: {
manual: true, manual: true,
@ -926,28 +1077,58 @@ const ExtensionConf: SettingsInterface = {
} }
}, },
"www.twitch.tv": { "www.twitch.tv": {
enable: ExtensionMode.All, enable: {
enableAard: ExtensionMode.All, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.All, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.FullScreen, normal: ExtensionMode.Enabled,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
type: SiteSupportLevel.OfficialSupport, type: 'official',
defaultType: SiteSupportLevel.OfficialSupport, defaultType: 'official',
}, },
"old.reddit.com" : { "old.reddit.com" : {
enable: ExtensionMode.Disabled, enable: {
enableAard: ExtensionMode.Disabled, fullscreen: ExtensionMode.Disabled,
enableKeyboard: ExtensionMode.Disabled, theater: ExtensionMode.Disabled,
enableUI: ExtensionMode.Disabled, normal: ExtensionMode.Disabled,
},
enableAard: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled,
},
enableUI: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Never, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Never,
type: SiteSupportLevel.OfficialBlacklist, type: 'officially-disabled',
defaultType: SiteSupportLevel.OfficialBlacklist, defaultType: 'officially-disabled',
activeDOMConfig: 'official', activeDOMConfig: 'official',
DOMConfig: { DOMConfig: {
'official': { 'official': {
type: SiteSupportLevel.OfficialSupport, type: 'official',
// customCss: 'video {\n width: 100% !important;\n height: 100% !important;\n}', // customCss: 'video {\n width: 100% !important;\n height: 100% !important;\n}',
elements: { elements: {
player: { player: {
@ -959,18 +1140,33 @@ const ExtensionConf: SettingsInterface = {
}, },
}, },
"www.reddit.com" : { "www.reddit.com" : {
enable: ExtensionMode.Disabled, enable: {
enableAard: ExtensionMode.Disabled, fullscreen: ExtensionMode.Disabled,
enableKeyboard: ExtensionMode.Disabled, theater: ExtensionMode.Disabled,
enableUI: ExtensionMode.Disabled, normal: ExtensionMode.Disabled,
},
enableAard: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled,
},
enableUI: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Never, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Never,
type: SiteSupportLevel.OfficialBlacklist, type: 'officially-disabled',
defaultType: SiteSupportLevel.OfficialBlacklist, defaultType: 'officially-disabled',
activeDOMConfig: 'official', activeDOMConfig: 'official',
DOMConfig: { DOMConfig: {
'official': { 'official': {
type: SiteSupportLevel.OfficialSupport, type: 'official',
// customCss: 'video {\n width: 100% !important;\n height: 100% !important;\n}', // customCss: 'video {\n width: 100% !important;\n height: 100% !important;\n}',
elements: { elements: {
player: { player: {
@ -982,28 +1178,58 @@ const ExtensionConf: SettingsInterface = {
}, },
}, },
"imgur.com": { "imgur.com": {
enable: ExtensionMode.Disabled, enable: {
enableAard: ExtensionMode.Disabled, fullscreen: ExtensionMode.Disabled,
enableKeyboard: ExtensionMode.Disabled, theater: ExtensionMode.Disabled,
enableUI: ExtensionMode.Disabled, normal: ExtensionMode.Disabled,
},
enableAard: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled,
},
enableUI: {
fullscreen: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Never, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Never,
type: SiteSupportLevel.OfficialBlacklist, type: 'officially-disabled',
defaultType: SiteSupportLevel.OfficialBlacklist, defaultType: 'officially-disabled',
}, },
"www.wakanim.tv": { "www.wakanim.tv": {
enable: ExtensionMode.All, enable: {
enableAard: ExtensionMode.All, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.All, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.FullScreen, normal: ExtensionMode.Enabled,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
type: SiteSupportLevel.CommunitySupport, type: 'community',
defaultType: SiteSupportLevel.CommunitySupport, defaultType: 'community',
activeDOMConfig: 'community', activeDOMConfig: 'community',
DOMConfig: { DOMConfig: {
'community': { 'community': {
type: SiteSupportLevel.CommunitySupport, type: 'community',
elements: { elements: {
player: { player: {
manual: true, manual: true,
@ -1014,34 +1240,65 @@ const ExtensionConf: SettingsInterface = {
}, },
}, },
"app.plex.tv": { "app.plex.tv": {
enable: ExtensionMode.Theater, enable: {
enableAard: ExtensionMode.Theater, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.Theater, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.FullScreen, normal: ExtensionMode.Disabled,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
type: SiteSupportLevel.CommunitySupport, type: 'community',
defaultType: SiteSupportLevel.CommunitySupport, defaultType: 'community',
activeDOMConfig: 'community', activeDOMConfig: 'community',
DOMConfig: { DOMConfig: {
'community': { 'community': {
type: SiteSupportLevel.CommunitySupport, type: 'community',
// customCss: "body {\n background-color: #000;\n}\n\n.application {\n background-color: #000;\n}" // customCss: "body {\n background-color: #000;\n}\n\n.application {\n background-color: #000;\n}"
} }
} }
}, },
"metaivi.com": { "metaivi.com": {
enable: ExtensionMode.Theater, enable: {
enableAard: ExtensionMode.Theater, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.Theater, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.FullScreen, normal: ExtensionMode.Disabled,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
type: SiteSupportLevel.CommunitySupport, type: "community",
defaultType: SiteSupportLevel.CommunitySupport, defaultType: "community",
activeDOMConfig: 'community', activeDOMConfig: 'community',
DOMConfig: { DOMConfig: {
'community': { 'community': {
type: SiteSupportLevel.CommunitySupport, type: 'community',
elements: { elements: {
video: { video: {
nodeCss: {'position': 'absolute !important'} nodeCss: {'position': 'absolute !important'}
@ -1051,18 +1308,33 @@ const ExtensionConf: SettingsInterface = {
}, },
}, },
"piped.kavin.rocks": { "piped.kavin.rocks": {
enable: ExtensionMode.Theater, enable: {
enableAard: ExtensionMode.Theater, fullscreen: ExtensionMode.Enabled,
enableKeyboard: ExtensionMode.Theater, theater: ExtensionMode.Enabled,
enableUI: ExtensionMode.FullScreen, normal: ExtensionMode.Disabled,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always, applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy.Always,
type: SiteSupportLevel.CommunitySupport, type: "community",
defaultType: SiteSupportLevel.CommunitySupport, defaultType: "community",
activeDOMConfig: 'community', activeDOMConfig: 'community',
DOMConfig: { DOMConfig: {
'community': { 'community': {
type: SiteSupportLevel.CommunitySupport, type: 'community',
// customCss: ".shaka-video-container {\n flex-direction: column !important;\n}" // customCss: ".shaka-video-container {\n flex-direction: column !important;\n}"
} }
} }

View File

@ -263,7 +263,7 @@ export class Aard {
// console.log('--- video data: ---\n', this.videoData); // console.log('--- video data: ---\n', this.videoData);
return; return;
} }
if (this.siteSettings.data.enableAard <= this.videoData.player.environment) { if (this.siteSettings.data.enableAard[this.videoData.player.environment] === ExtensionMode.Enabled) {
this.start(); this.start();
} else { } else {
this.stop(); this.stop();

View File

@ -1,4 +1,5 @@
import AspectRatioType from '@src/common/enums/AspectRatioType.enum'; import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
import ExtensionMode from '@src/common/enums/ExtensionMode.enum';
import { ArVariant } from '@src/common/interfaces/ArInterface'; import { ArVariant } from '@src/common/interfaces/ArInterface';
import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface'; import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
import EventBus from '../EventBus'; import EventBus from '../EventBus';
@ -15,10 +16,15 @@ import { GlDebugCanvas, GlDebugType } from './gl/GlDebugCanvas';
import { AardCanvasStore } from './interfaces/aard-canvas-store.interface'; import { AardCanvasStore } from './interfaces/aard-canvas-store.interface';
import { AardDetectionSample, generateSampleArray, resetSamples } from './interfaces/aard-detection-sample.interface'; import { AardDetectionSample, generateSampleArray, resetSamples } from './interfaces/aard-detection-sample.interface';
import { AardStatus, initAardStatus } from './interfaces/aard-status.interface'; import { AardStatus, initAardStatus } from './interfaces/aard-status.interface';
import { AardTestResult_SubtitleRegion, AardTestResults, resetSubtitleScanResults } from './interfaces/aard-test-results.interface';
import { AardTimers, initAardTimers } from './interfaces/aard-timers.interface'; import { AardTimers, initAardTimers } from './interfaces/aard-timers.interface';
import { ComponentLogger } from '../logging/ComponentLogger'; import { ComponentLogger } from '../logging/ComponentLogger';
import { AardPollingOptions } from './enums/aard-polling-options.enum'; import { AardPollingOptions } from './enums/aard-polling-options.enum';
import { AardSubtitleCropMode } from './enums/aard-subtitle-crop-mode.enum';
import { LetterboxOrientation } from './enums/letterbox-orientation.enum';
import { Edge } from './enums/edge.enum';
import { AardUncertainReason } from './enums/aard-letterbox-uncertain-reason.enum'; import { AardUncertainReason } from './enums/aard-letterbox-uncertain-reason.enum';
import { result } from 'lodash';
import { AardLegacyTestResults, initAardTestResults, resetAardTestResults, resetGuardLine } from './interfaces/aard-legacy-test-results.interface'; import { AardLegacyTestResults, initAardTestResults, resetAardTestResults, resetGuardLine } from './interfaces/aard-legacy-test-results.interface';
@ -433,7 +439,7 @@ export class AardLegacy {
// console.log('--- video data: ---\n', this.videoData); // console.log('--- video data: ---\n', this.videoData);
return; return;
} }
if (this.videoData.player.environment <= this.siteSettings.data.enableAard) { if (this.siteSettings.data.enableAard[this.videoData.player.environment] === ExtensionMode.Enabled) {
this.start(); this.start();
} else { } else {
this.stop(); this.stop();

View File

@ -24,6 +24,10 @@ const BASE_LOGGING_STYLES = {
*/ */
export class KeyboardHandler extends KbmBase { export class KeyboardHandler extends KbmBase {
listenFor: string[] = ['keyup']; listenFor: string[] = ['keyup'];
settings: Settings;
siteSettings: SiteSettings;
eventBus: EventBus;
playerElements: HTMLElement[] = []; playerElements: HTMLElement[] = [];
@ -103,8 +107,8 @@ export class KeyboardHandler extends KbmBase {
} }
} }
setKeyboardLocal(state: ExtensionMode) { setKeyboardLocal(state) {
if (state === ExtensionMode.All) { if (state === ExtensionMode.Enabled) {
this.keyboardLocalDisabled = false; this.keyboardLocalDisabled = false;
} else if (state === ExtensionMode.Disabled) { } else if (state === ExtensionMode.Disabled) {
this.keyboardLocalDisabled = true; this.keyboardLocalDisabled = true;

View File

@ -113,6 +113,7 @@ export class LogAggregator {
private storageChangeListener(changes, area) { private storageChangeListener(changes, area) {
if (!changes[STORAGE_LOG_SETTINGS_KEY]) { if (!changes[STORAGE_LOG_SETTINGS_KEY]) {
// console.info('We dont have any logging settings, not processing frther', changes);
return; return;
} }

View File

@ -6,10 +6,9 @@ import ExtensionConfPatch from '../../conf/ExtConfPatches';
import SettingsInterface from '../../../common/interfaces/SettingsInterface'; import SettingsInterface from '../../../common/interfaces/SettingsInterface';
import AspectRatioType from '../../../common/enums/AspectRatioType.enum'; import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import { GetSiteSettingsOptions, SiteSettings } from './SiteSettings'; import { GetSiteSettingsOptions, SiteSettings } from './SiteSettings';
import { SettingsSnapshot, SettingsSnapshotManager } from './SettingsSnapshotManager'; import { SettingsSnapshotManager } from './SettingsSnapshotManager';
import { ComponentLogger } from '../logging/ComponentLogger'; import { ComponentLogger } from '../logging/ComponentLogger';
import { LogAggregator } from '../logging/LogAggregator'; import { LogAggregator } from '../logging/LogAggregator';
import { getDiff } from 'json-difference'
if(process.env.CHANNEL !== 'stable'){ if(process.env.CHANNEL !== 'stable'){
console.info("Loading Settings"); console.info("Loading Settings");
@ -96,49 +95,28 @@ class Settings {
if (!changes.uwSettings) { if (!changes.uwSettings) {
return; return;
} }
console.log('new settings change.')
this.logger?.info('storageOnChange', "Settings have been changed outside of here. Updating active settings. Changes:", changes, "storage area:", area); this.logger?.info('storageOnChange', "Settings have been changed outside of here. Updating active settings. Changes:", changes, "storage area:", area);
// if (changes['uwSettings'] && changes['uwSettings'].newValue) { // if (changes['uwSettings'] && changes['uwSettings'].newValue) {
// this.logger?.log('info', 'settings',"[Settings::<storage/on change>] new settings object:", JSON.parse(changes.uwSettings.newValue)); // this.logger?.log('info', 'settings',"[Settings::<storage/on change>] new settings object:", JSON.parse(changes.uwSettings.newValue));
// } // }
const parsedSettings = JSON.parse(changes.uwSettings.newValue); const parsedSettings = JSON.parse(changes.uwSettings.newValue);
const diff = getDiff(this.active, parsedSettings, {isLodashLike: true});
const validChangeFn = (x: any) =>
x[0] !== 'lastModified';
const validAdditions = diff.added.filter(validChangeFn);
const validEdits = diff.edited.filter(validChangeFn);
const validRemovals = diff.removed.filter(validChangeFn).filter(x => x[0] !== 'version');
this.logger?.info('storageOnChange', "Diff between current and changed settings:", diff, 'valid changes:', {validAdditions, validEdits, validRemovals});
if (
validAdditions.length === 0
&& validRemovals.length === 0
&& validEdits.length === 0
) {
this.logger?.warn('storageOnChange', "storageChangeListener fired, but no changes were detected. Sus.\nchanges:", changes, "storage area:", area, 'current:', this.active, 'diff:', diff);
return;
}
this.setActive(parsedSettings); this.setActive(parsedSettings);
this.logger?.info('storageOnChange', 'Does parsedSettings.preventReload exist?', parsedSettings.preventReload, "Does callback exist?", !!this.onSettingsChanged); this.logger?.info('storageOnChange', 'Does parsedSettings.preventReload exist?', parsedSettings.preventReload, "Does callback exist?", !!this.onSettingsChanged);
if (!parsedSettings.preventReload) { if (!parsedSettings.preventReload) {
this.active.preventReload = true;
try { try {
for (const fn of this.onChangedCallbacks) { for (const fn of this.onChangedCallbacks) {
try { try {
fn(); fn();
} catch (e) { } catch (e) {
this.logger?.warn('storageOnChange', "afterSettingsChanged fallback failed. It's possible that a vue component got destroyed, and this function is nothing more than vestigial remains. It would be nice if we implemented something that allows us to remove callback functions from array, and remove vue callbacks from the callback array when their respective UI component gets destroyed. Or this could be an error with the function itself. IDK, here's the error.", e) this.logger?.warn('storageOnChange', "afterSettingsChanged fallback failed. It's possible that a vue component got destroyed, and this function is nothing more than vestigal remains. It would be nice if we implemented something that allows us to remove callback functions from array, and remove vue callbacks from the callback array when their respective UI component gets destroyed. Or this could be an error with the function itself. IDK, here's the error.", e)
} }
} }
if (this.onSettingsChanged) { if (this.onSettingsChanged) {
this.onSettingsChanged(); this.onSettingsChanged();
} }
this.logger?.info('storageOnChange', 'Update callback finished.') this.logger?.info('storageOnChange', 'Update callback finished.')
} catch (e) { } catch (e) {
this.logger?.error('storageOnChange', "CALLING UPDATE CALLBACK FAILED. Reason:", e) this.logger?.error('storageOnChange', "CALLING UPDATE CALLBACK FAILED. Reason:", e)
@ -148,7 +126,7 @@ class Settings {
try { try {
fn(); fn();
} catch (e) { } catch (e) {
this.logger?.warn('storageOnChange', "afterSettingsChanged fallback failed. It's possible that a vue component got destroyed, and this function is nothing more than vestigial remains. It would be nice if we implemented something that allows us to remove callback functions from array, and remove vue callbacks from the callback array when their respective UI component gets destroyed. Or this could be an error with the function itself. IDK, here's the error.", e) this.logger?.warn('storageOnChange', "afterSettingsChanged fallback failed. It's possible that a vue component got destroyed, and this function is nothing more than vestigal remains. It would be nice if we implemented something that allows us to remove callback functions from array, and remove vue callbacks from the callback array when their respective UI component gets destroyed. Or this could be an error with the function itself. IDK, here's the error.", e)
} }
} }
if (this.afterSettingsSaved) { if (this.afterSettingsSaved) {
@ -234,7 +212,7 @@ class Settings {
private findFirstNecessaryPatch(version) { private findFirstNecessaryPatch(version) {
return ExtensionConfPatch.findIndex(x => this.compareExtensionVersions(x.forVersion, version) > 0); return ExtensionConfPatch.findIndex(x => this.compareExtensionVersions(x.forVersion, version) > 0);
} }
private applySettingsPatches(currentSettings, oldVersion, options?: {skipSnapshot?: boolean}) { private applySettingsPatches(oldVersion) {
let index = this.findFirstNecessaryPatch(oldVersion); let index = this.findFirstNecessaryPatch(oldVersion);
if (index === -1) { if (index === -1) {
@ -243,30 +221,26 @@ class Settings {
} }
// save current settings object // save current settings object
if (!options?.skipSnapshot) { const currentSettings = this.active;
this.snapshotManager.createSnapshot(
JSON.parse(JSON.stringify(currentSettings)), this.snapshotManager.createSnapshot(
{ JSON.parse(JSON.stringify(currentSettings)),
label: 'Pre-migration snapshot', {
isAutomatic: true label: 'Pre-migration snapshot',
} isAutomatic: true
); }
} );
// this.active.newFeatureTracker = {};
// apply all remaining patches // apply all remaining patches
const pendingPatchesCount = ExtensionConfPatch.length - index; this.logger?.info('applySettingsPatches', `There are ${ExtensionConfPatch.length - index} settings patches to apply`);
this.logger?.info('applySettingsPatches', `There are ${pendingPatchesCount} settings patches to apply`);
const defaultSettings = this.getDefaultSettings();
while (index < ExtensionConfPatch.length) { while (index < ExtensionConfPatch.length) {
const updateFn = ExtensionConfPatch[index].updateFn; const updateFn = ExtensionConfPatch[index].updateFn;
this.logger.info('applySettingsPatches', `Processing patch ${index} / ${ExtensionConfPatch.length}. Patch ${!!updateFn ? 'has' : 'does NOT have'} an update function.`);
if (updateFn) { if (updateFn) {
try { try {
this.logger.log('applySettingsPatches', `Starting to update patch for version ${ExtensionConfPatch[index].forVersion} (${pendingPatchesCount - (ExtensionConfPatch.length - index)} / ${pendingPatchesCount}) | index: ${index}, patches length: ${ExtensionConfPatch.length}`); updateFn(this.active, this.getDefaultSettings());
updateFn(currentSettings, defaultSettings, this.logger);
this.logger.log('applySettingsPatches', `Patch for version ${ExtensionConfPatch[index].forVersion} applied. (${pendingPatchesCount - (ExtensionConfPatch.length - index)} / ${pendingPatchesCount})`);
} catch (e) { } catch (e) {
this.logger?.error('applySettingsPatches', 'Failed to execute update function. Keeping settings object as-is. Error:', e); this.logger?.error('applySettingsPatches', 'Failed to execute update function. Keeping settings object as-is. Error:', e);
} }
@ -274,37 +248,30 @@ class Settings {
index++; index++;
} }
return currentSettings;
} }
async init(options?: {dryRun?: boolean, snapshot?: SettingsSnapshot, skipSnapshot?: boolean}) { async init() {
let settings; let settings = await this.get();
if (options?.snapshot) { if (settings?.dev?.loadFromSnapshot) {
this.logger?.info('init', 'Snapshot was provided — will load settings from snapshot.', options.snapshot);
settings = options.snapshot.settings;
} else if (settings?.dev?.loadFromSnapshot) {
this.logger?.info('init', 'Dev mode is enabled, Loading settings from snapshot:', settings.dev.loadFromSnapshot); this.logger?.info('init', 'Dev mode is enabled, Loading settings from snapshot:', settings.dev.loadFromSnapshot);
const snapshot = await this.snapshotManager.getSnapshot(); const snapshot = await this.snapshotManager.getSnapshot();
if (snapshot) { if (snapshot) {
settings = snapshot.settings; settings = snapshot.settings;
} }
} else {
settings = await this.get();
} }
const currentVersion = this.getExtensionVersion(); this.version = this.getExtensionVersion();
// |—> on first setup, settings is undefined & settings.version is haram // |—> on first setup, settings is undefined & settings.version is haram
// | since new installs ship with updates by default, no patching is // | since new installs ship with updates by default, no patching is
// | needed. In this case, we assume we're on the current version // | needed. In this case, we assume we're on the current version
const oldVersion = settings?.version ?? currentVersion; const oldVersion = settings?.version ?? this.version;
if (settings) { if (settings) {
this.logger?.info('init', "Configuration fetched from storage:", settings, this.logger?.info('init', "Configuration fetched from storage:", settings,
"\nlast saved with:", settings.version, "\nlast saved with:", settings.version,
"\ncurrent version:", currentVersion "\ncurrent version:", this.version
); );
} }
@ -317,57 +284,40 @@ class Settings {
'\nsettings:', '\nsettings:',
settings settings
); );
this.active = this.getDefaultSettings();
this.active.version = this.version;
await this.save();
settings = this.getDefaultSettings(); return this.active;
settings.version = currentVersion;
if (!options?.dryRun) {
this.active = settings;
this.version = currentVersion;
await this.save();
}
return settings;
} }
// if there's settings, set saved object as active settings // if there's settings, set saved object as active settings
this.active = settings;
// if version number is undefined, we make it defined // if version number is undefined, we make it defined
// this should only happen on first extension initialization // this should only happen on first extension initialization
if (!settings.version) { if (!this.active.version) {
settings.version = currentVersion; this.active.version = this.version;
await this.save();
if (!options?.dryRun) { return this.active;
this.active = settings;
this.version = currentVersion;
await this.save();
}
return settings;
} }
// check if extension has been updated. If not, return settings as they were retrieved // check if extension has been updated. If not, return settings as they were retrieved
if (settings.version === currentVersion) { if (this.active.version === this.version) {
this.logger?.info('init', "extension was saved with current version of ultrawidify. Returning object as-is."); this.logger?.info('init', "extension was saved with current version of ultrawidify. Returning object as-is.");
if (!options?.dryRun) { return this.active;
this.active = settings;
}
return settings;
} }
// in case settings in previous version contained a fucky wucky, we overwrite existing settings with a patch // in case settings in previous version contained a fucky wucky, we overwrite existing settings with a patch
settings = this.applySettingsPatches(settings, oldVersion, {skipSnapshot: options?.dryRun || options?.skipSnapshot}); this.applySettingsPatches(oldVersion);
// set 'whatsNewChecked' flag to false when updating, always // set 'whatsNewChecked' flag to false when updating, always
settings.whatsNewChecked = false; this.active.whatsNewChecked = false;
// update settings version to current // update settings version to current
settings.version = currentVersion; this.active.version = this.version;
if (!options?.dryRun) { await this.save();
this.active = settings; return this.active;
this.version = currentVersion;
await this.save();
}
return settings;
} }
async get(): Promise<SettingsInterface | undefined> { async get(): Promise<SettingsInterface | undefined> {
@ -487,11 +437,6 @@ class Settings {
removeAfterChangeListener(fn: () => void): void { removeAfterChangeListener(fn: () => void): void {
this.afterSettingsChangedCallbacks = this.afterSettingsChangedCallbacks.filter(x => x !== fn); this.afterSettingsChangedCallbacks = this.afterSettingsChangedCallbacks.filter(x => x !== fn);
} }
async testMigration(snapshot: SettingsSnapshot) {
const afterMigrationSettings = await this.init({dryRun: true, snapshot});
this.logger.log('testMigration', 'Settings migrated. Settings model after initialization:', afterMigrationSettings);
}
} }
export default Settings; export default Settings;

View File

@ -1,12 +1,11 @@
import CropModePersistence from '../../../common/enums/CropModePersistence.enum'; import CropModePersistence from '../../../common/enums/CropModePersistence.enum';
import ExtensionMode from '../../../common/enums/ExtensionMode.enum'; import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
import { ExtensionEnvironment, SettingsReloadComponent, SettingsReloadFlags, SiteSettingsInterface } from '../../../common/interfaces/SettingsInterface'; import { SettingsReloadComponent, SettingsReloadFlags, SiteSettingsInterface } from '../../../common/interfaces/SettingsInterface';
import { _cp } from '../../../common/js/utils'; import { _cp } from '../../../common/js/utils';
import Settings from './Settings'; import Settings from './Settings';
import StretchType from '../../../common/enums/StretchType.enum'; import StretchType from '../../../common/enums/StretchType.enum';
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum'; import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
import EmbeddedContentSettingsOverridePolicy from '../../../common/enums/EmbeddedContentSettingsOverridePolicy.enum'; import EmbeddedContentSettingsOverridePolicy from '../../../common/enums/EmbeddedContentSettingsOverridePolicy.enum';
import { SiteSupportLevel } from '../../../common/enums/SiteSupportLevel.enum';
export interface GetSiteSettingsOptions { export interface GetSiteSettingsOptions {
@ -183,8 +182,6 @@ export class SiteSettings {
*/ */
private compileSettingsObject() { private compileSettingsObject() {
const {siteSettings, usesSettingsFor} = this.getSettingsForSite(this.options); const {siteSettings, usesSettingsFor} = this.getSettingsForSite(this.options);
console.log('got settings for site:', siteSettings, usesSettingsFor)
this.data = _cp(siteSettings); this.data = _cp(siteSettings);
this.usesSettingsFor = usesSettingsFor; this.usesSettingsFor = usesSettingsFor;
@ -216,10 +213,13 @@ export class SiteSettings {
} }
for (const enableSegment of ['enable', 'enableAard', 'enableKeyboard', 'enableUI']) { for (const enableSegment of ['enable', 'enableAard', 'enableKeyboard', 'enableUI']) {
if (this.data[enableSegment] === undefined) { if (!this.data[enableSegment]) {
this.data[enableSegment] = {}; this.data[enableSegment] = {};
} else if (this.data[enableSegment] === ExtensionMode.Default) { }
this.data[enableSegment] = this.defaultSettings[enableSegment]; for (const environment of ['normal', 'theater', 'fullscreen']) {
if (!this.data[enableSegment][environment] || this.data[enableSegment][environment] === ExtensionMode.Default) {
this.data[enableSegment][environment] = _cp(this.defaultSettings[enableSegment][environment]);
}
} }
} }
@ -339,14 +339,14 @@ export class SiteSettings {
} }
} }
private _getEnvironment(isTheater: boolean, isFullscreen: boolean): ExtensionEnvironment { private _getEnvironment(isTheater: boolean, isFullscreen: boolean): 'fullscreen' | 'theater' | 'normal' {
if (isFullscreen) { if (isFullscreen) {
return ExtensionEnvironment.Fullscreen; return 'fullscreen';
} }
if (isTheater) { if (isTheater) {
return ExtensionEnvironment.Theater; return 'theater';
} }
return ExtensionEnvironment.Normal; return 'normal';
} }
/** /**
@ -355,9 +355,9 @@ export class SiteSettings {
* @param isFullscreen * @param isFullscreen
* @returns ExtensionMode * @returns ExtensionMode
*/ */
isEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): boolean { isEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): ExtensionMode {
const env = this._getEnvironment(isTheater, isFullscreen); const env = this._getEnvironment(isTheater, isFullscreen);
return env <= this.data.enable; return this.data.enable[env];
} }
/** /**
@ -366,9 +366,9 @@ export class SiteSettings {
* @param isFullscreen * @param isFullscreen
* @returns * @returns
*/ */
isAardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): boolean { isAardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): ExtensionMode {
const env = this._getEnvironment(isTheater, isFullscreen); const env = this._getEnvironment(isTheater, isFullscreen);
return env <= this.data.enableAard; return this.data.enableAard[env];
} }
/** /**
@ -377,9 +377,9 @@ export class SiteSettings {
* @param isFullscreen * @param isFullscreen
* @returns * @returns
*/ */
isKeyboardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): boolean { isKeyboardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): ExtensionMode {
const env = this._getEnvironment(isTheater, isFullscreen); const env = this._getEnvironment(isTheater, isFullscreen);
return env <= this.data.enableKeyboard; return this.data.enableKeyboard[env];
} }
//#endregion //#endregion
@ -461,11 +461,11 @@ export class SiteSettings {
if (options.scripted && !this.settings.active.sites[targetSite]) { if (options.scripted && !this.settings.active.sites[targetSite]) {
this.settings.active.sites[targetSite] = _cp(this.settings.active.sites['@global']); this.settings.active.sites[targetSite] = _cp(this.settings.active.sites['@global']);
this.settings.active.sites[targetSite].autocreated = true; this.settings.active.sites[targetSite].autocreated = true;
this.settings.active.sites[targetSite].type = SiteSupportLevel.Unknown; this.settings.active.sites[targetSite].type = 'unknown';
} else { } else {
if (!this.settings.active.sites[targetSite] || this.settings.active.sites[targetSite].autocreated) { if (!this.settings.active.sites[targetSite] || this.settings.active.sites[targetSite].autocreated) {
this.settings.active.sites[targetSite] = _cp(this.data); this.settings.active.sites[targetSite] = _cp(this.data);
this.settings.active.sites[targetSite].type = SiteSupportLevel.UserDefined; this.settings.active.sites[targetSite].type = 'user-defined';
} }
} }

View File

@ -41,7 +41,7 @@ class UI {
// TODO: at some point, UI should be different for global popup and in-player UI // TODO: at some point, UI should be different for global popup and in-player UI
this.csuiScheme = this.getCsuiScheme(); this.csuiScheme = this.getCsuiScheme();
const csuiVersion = this.getCsuiVersion(this.csuiScheme.contentScheme); const csuiVersion = this.getCsuiVersion(this.csuiScheme.contentScheme);
// this.uiURI = chrome.runtime.getURL(`/csui/${csuiVersion}.html`); this.uiURI = chrome.runtime.getURL(`/csui/${csuiVersion}.html`);
this.extensionBase = chrome.runtime.getURL('').replace(/\/$/, ""); this.extensionBase = chrome.runtime.getURL('').replace(/\/$/, "");
// UI will be initialized when setUiVisibility is called // UI will be initialized when setUiVisibility is called
@ -53,13 +53,17 @@ class UI {
return true; return true;
} }
return this.siteSettings?.data.enableUI !== ExtensionMode.Disabled; return this.siteSettings?.data.enableUI.fullscreen === ExtensionMode.Enabled
|| this.siteSettings?.data.enableUI.theater === ExtensionMode.Enabled
|| this.siteSettings?.data.enableUI.normal === ExtensionMode.Enabled;
} }
async init() { async init() {
if (!this.canRun()) { if (!this.canRun()) {
// console.log('ui config: canRun returned false', this.siteSettings?.data.enableUI.fullscreen === ExtensionMode.Enabled, this.siteSettings?.data.enableUI.theater === ExtensionMode.Enabled, this.siteSettings?.data.enableUI.normal === ExtensionMode.Enabled)
return; return;
} }
// console.log('ui config: canRun returned truie', this.siteSettings?.data.enableUI.fullscreen === ExtensionMode.Enabled, this.siteSettings?.data.enableUI.theater === ExtensionMode.Enabled, this.siteSettings?.data.enableUI.normal === ExtensionMode.Enabled)
this.initUIContainer(); this.initUIContainer();
@ -119,7 +123,6 @@ class UI {
} }
loadIframe() { loadIframe() {
return;
// in onMouseMove, we currently can't access this because we didn't // in onMouseMove, we currently can't access this because we didn't
// do things the most properly // do things the most properly
const uiURI = this.uiURI; const uiURI = this.uiURI;
@ -339,7 +342,7 @@ class UI {
canShowUI: false, canShowUI: false,
} }
if (this.playerData?.environment && this.playerData?.environment <= this.siteSettings.data.enableUI) { if (this.playerData?.environment && this.siteSettings.data.enableUI[this.playerData?.environment] !== ExtensionMode.Enabled) {
return result; return result;
} }
@ -474,7 +477,7 @@ class UI {
} }
/** /**
* Sends message to iframe. Messages sent with this function will be routed to eventBus. * Sends message to iframe. Messages sent with this function will be routed to eventbus.
*/ */
sendToIframe(action, actionConfig, routingData, uiURI = this.uiURI) { sendToIframe(action, actionConfig, routingData, uiURI = this.uiURI) {
// if (routingData) { // if (routingData) {

View File

@ -1,3 +1,4 @@
import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
import EventBus from '../EventBus'; import EventBus from '../EventBus';
import { SiteSettings } from '../settings/SiteSettings'; import { SiteSettings } from '../settings/SiteSettings';
@ -18,9 +19,9 @@ export class ExtensionStatus {
return this.fsStatus.fullscreen; return this.fsStatus.fullscreen;
} }
private enabled: boolean; private enabled: ExtensionMode;
private aardEnabled: boolean; private aardEnabled: ExtensionMode;
private kbdEnabled: boolean; private kbdEnabled: ExtensionMode;
constructor(siteSettings: SiteSettings, eventBus: EventBus, fsStatus: {fullscreen: boolean}){ constructor(siteSettings: SiteSettings, eventBus: EventBus, fsStatus: {fullscreen: boolean}){
this.siteSettings = siteSettings; this.siteSettings = siteSettings;
@ -32,20 +33,26 @@ export class ExtensionStatus {
const canAard = this.siteSettings.isAardEnabledForEnvironment(this.isTheaterMode, this.isFullScreen); const canAard = this.siteSettings.isAardEnabledForEnvironment(this.isTheaterMode, this.isFullScreen);
const canKbd = this.siteSettings.isKeyboardEnabledForEnvironment(this.isTheaterMode, this.isFullScreen); const canKbd = this.siteSettings.isKeyboardEnabledForEnvironment(this.isTheaterMode, this.isFullScreen);
if (canRun) { if (canRun !== this.enabled) {
this.eventBus.send('set-extension-active', {}); if (canRun === ExtensionMode.Enabled) {
} else { this.eventBus.send('set-extension-active', {});
this.eventBus.send('set-extension-inactive', {}); } else {
this.eventBus.send('set-extension-inactive', {});
}
} }
if (canAard) { if (canAard !== this.aardEnabled) {
this.eventBus.send('set-aard-active', {}); if (canAard === ExtensionMode.Enabled) {
} else { this.eventBus.send('set-aard-active', {});
this.eventBus.send('set-aard-inactive', {}); } else {
this.eventBus.send('set-aard-inactive', {});
}
} }
if (canKbd) { if (canKbd !== this.kbdEnabled) {
this.eventBus.send('set-kbd-active', {}); if (canKbd === ExtensionMode.Enabled) {
} else { this.eventBus.send('set-kbd-active', {});
this.eventBus.send('set-kbd-inactive', {}); } else {
this.eventBus.send('set-kbd-inactive', {});
}
} }
this.enabled = canRun; this.enabled = canRun;

View File

@ -269,14 +269,14 @@ class PlayerData {
* @returns * @returns
*/ */
deferredUiInitialization(playerDimensions = this.dimensions) { deferredUiInitialization(playerDimensions = this.dimensions) {
if (this.ui || this.siteSettings.data.enableUI === ExtensionMode.Disabled) { if (this.ui || this.siteSettings.data.enableUI.fullscreen === ExtensionMode.Disabled) {
return; return;
} }
if ( if (
this.isFullscreen this.isFullscreen
|| ( || (
this.siteSettings.data.enableUI >= ExtensionMode.FullScreen this.siteSettings.data.enableUI.theater !== ExtensionMode.Disabled
&& playerDimensions.width > 1208 && playerDimensions.width > 1208
&& playerDimensions.height > 720 && playerDimensions.height > 720
) )
@ -478,7 +478,7 @@ class PlayerData {
this.isTooSmall = this.checkIfTooSmall(); this.isTooSmall = this.checkIfTooSmall();
} }
const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode); const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode) === ExtensionMode.Enabled;
if (this.runLevel === RunLevel.Off && canEnable) { if (this.runLevel === RunLevel.Off && canEnable) {
this.eventBus.send('restore-ar', null); this.eventBus.send('restore-ar', null);

View File

@ -408,7 +408,7 @@ class VideoData {
this.logger.warn('onEnvironmentChanged', 'environment changed from:', this.currentEnvironment, 'to:', this.player.environment); this.logger.warn('onEnvironmentChanged', 'environment changed from:', this.currentEnvironment, 'to:', this.player.environment);
this.currentEnvironment = this.player.environment; this.currentEnvironment = this.player.environment;
if (this.player.environment <= this.siteSettings.data.enable) { if (this.siteSettings.data.enable[this.player.environment] === ExtensionMode.Disabled) {
this.setRunLevel(RunLevel.Off); this.setRunLevel(RunLevel.Off);
} else { } else {
this.restoreAr(); this.restoreAr();
@ -417,7 +417,7 @@ class VideoData {
} }
setRunLevel(runLevel: RunLevel, options?: {fromPlayer?: boolean}) { setRunLevel(runLevel: RunLevel, options?: {fromPlayer?: boolean}) {
if (this.player && this.siteSettings.data.enable >= this.player.environment) { if (this.player && this.siteSettings.data.enable[this.player.environment] !== ExtensionMode.Enabled) {
runLevel = RunLevel.Off; runLevel = RunLevel.Off;
} }

View File

@ -1,11 +1,27 @@
@import "tailwindcss"; @import "tailwindcss";
@import "tailwindcss/utilities"; @import "tailwindcss/utilities";
@import '@ui/res/styles/theme.css'; @theme {
@import '@ui/res/styles/forms.css'; --breakpoint-popup: 640px;
@import '@ui/res/styles/fonts.css'; --breakpoint-window: 960px;
@import '@ui/res/styles/buttons.css';
--color-primary: #ff872c;
--color-primary-50: #ffddbe;
--color-primary-100: #ffcda0;
--color-primary-200: #ffbd83;
--color-primary-300: #ffac66;
--color-primary-400: #ff9a4a;
--color-primary-500: #ff872c;
--color-primary-600: #de7622;
--color-primary-700: #be6518;
--color-primary-800: #9f540e;
--color-primary-900: #824406;
--color-primary-950: #663400;
}
@import './ui/res/styles/forms.css';
@import './ui/res/styles/fonts.css';
@import './ui/res/styles/buttons.css';
html, body { html, body {
@apply bg-stone-950 text-stone-300; @apply bg-stone-950 text-stone-300;

View File

@ -2,7 +2,7 @@
"manifest_version": 3, "manifest_version": 3,
"name": "Ultrawidify", "name": "Ultrawidify",
"description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.", "description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.",
"version": "6.3.994", "version": "6.3.993",
"icons": { "icons": {
"32":"res/icons/uw-32.png", "32":"res/icons/uw-32.png",
"64":"res/icons/uw-64.png" "64":"res/icons/uw-64.png"
@ -19,8 +19,7 @@
"common/lib/browser-polyfill.js", "common/lib/browser-polyfill.js",
"ext/uw.js" "ext/uw.js"
], ],
"all_frames": true, "all_frames": true
"type": "module"
}], }],
"minimum_chrome_version": "93", "minimum_chrome_version": "93",

View File

@ -232,8 +232,8 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import BrowserDetect from '@src/ext/conf/BrowserDetect'; import BrowserDetect from '../../../ext/conf/BrowserDetect';
import JsonEditor from '@components/common/JsonEditor.vue'; import JsonEditor from '@csui/src/components/JsonEditor';
import {AardPollingOptions} from '@src/ext/lib/aard/enums/aard-polling-options.enum'; import {AardPollingOptions} from '@src/ext/lib/aard/enums/aard-polling-options.enum';
import {AardSubtitleCropMode} from '@src/ext/lib/aard/enums/aard-subtitle-crop-mode.enum'; import {AardSubtitleCropMode} from '@src/ext/lib/aard/enums/aard-subtitle-crop-mode.enum';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';

View File

@ -0,0 +1,103 @@
<template>
<div>
<div>
<h1>{{loggerPanel.title}}<small><br/>{{loggerPanel.subtitle}}</small></h1>
<div>Logger configuration:</div>
<JsonEditor
v-model="lastSettings"
></JsonEditor>
<div class="flex flex-row flex-end">
<div class="button" @click="getLoggerSettings()">
Revert
</div>
<div class="button button-primary" @click="saveLoggerSettings()">
Save
</div>
<div class="button button-primary" @click="startLogging()">
Save and start logging
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { LogAggregator, BLANK_LOGGER_CONFIG } from '@src/ext/lib/logging/LogAggregator';
import JsonEditor from '@csui/src/components/JsonEditor';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
JsonEditor
},
data() {
return {
loggerPanelRotation: [{
title: 'DEFORESTATOR 5000',
subtitle: 'Iron Legion\'s finest logging tool'
},{
title: 'Astinus',
subtitle: 'Ultrawidify logging tool'
}, {
title: 'Tracer',
subtitle: "Maybe I'll be Tracer — I'm already printing stack traces."
}, {
title: 'Grûmsh',
subtitle: "He who watches (all the things printed to the console)."
}, {
title: 'Situation Room/The Council',
subtitle: 'We will always be watching.'
}, {
title: 'Isengard Land Management Services',
subtitle: "#log4saruman"
}, {
title: 'Saw Hero',
subtitle: 'Ultrawidify logging tool'
}],
loggerPanel: {
title: 'whoopsie daisy',
subtitle: 'you broke the header choosing script',
pasteConfMode: false,
},
lastSettings: undefined,
parsedSettings: undefined,
currentSettings: undefined
};
},
props: [
'settings',
'eventBus',
],
created() {
this.loggerPanel = {
...this.loggerPanelRotation[Math.floor(Math.random() * this.loggerPanelRotation.length)],
pasteConfMode: false,
};
this.getLoggerSettings();
},
methods: {
loadDefaultConfig() {
this.lastSettings = JSON.parse(JSON.stringify(BLANK_LOGGER_CONFIG));
},
async getLoggerSettings() {
this.lastSettings = await LogAggregator.getConfig() || BLANK_LOGGER_CONFIG;
},
async saveLoggerSettings() {
console.log('Saving logger settings', this.lastSettings);
await LogAggregator.saveConfig({...this.lastSettings});
console.log('[ok] logger settings saved');
},
async startLogging(){
this.logStringified = undefined;
await LogAggregator.saveConfig({...this.lastSettings});
window.location.reload();
},
}
});
</script>

View File

@ -2,27 +2,19 @@
<p class="mb-4">Full changelog for older versions <a href="https://github.com/tamius-han/ultrawidify/blob/master/CHANGELOG.md" target="_blank">is available here</a>.</p> <p class="mb-4">Full changelog for older versions <a href="https://github.com/tamius-han/ultrawidify/blob/master/CHANGELOG.md" target="_blank">is available here</a>.</p>
<p>Changes in version <b class="font-semibold text-primary-400">6.4.0</b>:</p> <p>Changes in version <b class="font-semibold text-primary-400">6.4.0</b>:</p>
<b class="text-white">Potentially breaking:</b>
<ul>
<li>De-spaghettified the part of the settings that controls whether extension runs on a given site or not.</li>
</ul>
<b class="text-white">Autodetection:</b>
<ul> <ul>
<li>In-player UI can now appear in full-screen for websites that put players into top layer (allegedly).</li>
<li>Embedded sites now inherit settings of the parent frame. <small>However, this hasn't been tested for all edge cases and may contain bugs.</small></li>
<li>Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.</li>
<li>Autodetection can be set to stop after first aspect ratio detection, or after a period of no changes.</li> <li>Autodetection can be set to stop after first aspect ratio detection, or after a period of no changes.</li>
<li> <li>
There is a new, experimental mode for autodetection. At this point, it can be manually enabled in the autodetection settings. It will become the default option in 2026.<br/> There is a new, experiental mode for autodetection. At this point, it can be manually enabled in the autodetection settings. It will become the default option in 2026.<br/>
If you enable experimental mode, please consider reporting problems <a href="https://github.com/tamius-han/ultrawidify/issues/291" target="_blank">in this thread</a> on Github. If you enable experimental mode, please consider reporting problems <a href="https://github.com/tamius-han/ultrawidify/issues/291" target="_blank">in this thread</a> on Github.
</li> </li>
<li> <li>
Experimental autodetection can now detect hardcoded subtitles. You may change how extension handles subtitles in the settings. Autodetection can now detect hardcoded subtitles. You may change how extension handles subtitles in the settings.
Use of new experimental autodetection is required for subtitle handling options to work.
</li> </li>
</ul>
<b class="text-white">UI</b>
<ul>
<li>In-player UI can now appear in full-screen for websites that put players into top layer (allegedly).</li>
<li> <li>
Keyboard shortcut settings have been split from UI settings, and are now presented in list form on the settings page. Keyboard shortcut settings have been split from UI settings, and are now presented in list form on the settings page.
</li> </li>
@ -31,12 +23,6 @@
</li> </li>
</ul> </ul>
<b class="text-white">Other updates and fixes:</b>
<ul>
<li>Embedded sites now inherit settings of the parent frame. <small>However, this hasn't been tested for all edge cases and may contain bugs.</small></li>
<li>Added validation to custom aspect ratio entry menu. Corrected parsing of aspect ratios given in the X:Y format, even though aspect ratios should be ideally given as a single number.</li>
</ul>
</template> </template>
<script> <script>
import BrowserDetect from '@src/ext/conf/BrowserDetect'; import BrowserDetect from '@src/ext/conf/BrowserDetect';

View File

@ -153,9 +153,9 @@
</template> </template>
<script> <script>
import SiteExtensionSettings from '@components/segments/ExtensionSettings/Panels/SiteExtensionSettings.vue'; import SiteExtensionSettings from '@components/ExtensionSettings/Panels/SiteExtensionSettings.vue';
import FrameSiteSettings from '@components/segments/ExtensionSettings/Panels/FrameSiteSettings.vue'; import FrameSiteSettings from '@components/ExtensionSettings/Panels/FrameSiteSettings.vue';
import OtherSiteSettings from '@components/segments/ExtensionSettings/Panels/OtherSiteSettings.vue'; import OtherSiteSettings from '@components/ExtensionSettings/Panels/OtherSiteSettings.vue';
import Popup from '@csui/src/components/Popup'; import Popup from '@csui/src/components/Popup';
import ConfirmButton from '@csui/src/components/ConfirmButton'; import ConfirmButton from '@csui/src/components/ConfirmButton';
import UploadJsonFileButton from '@csui/src/components/UploadJsonFileButton'; import UploadJsonFileButton from '@csui/src/components/UploadJsonFileButton';

View File

@ -12,31 +12,31 @@
@click="setExtensionMode('enable', $event)" @click="setExtensionMode('enable', $event)"
> >
<option <option
v-if="simpleExtensionSettings.enable === 'invalid'" v-if="simpleExtensionSettings.enable === 'complex'"
:value="undefined" value="complex"
> >
(Site uses advanced settings) (Site uses advanced settings)
</option> </option>
<template v-if="isDefaultConfiguration"> <template v-if="isDefaultConfiguration">
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Disabled by default Disabled by default
</option> </option>
</template> </template>
<template v-else> <template v-else>
<option :value="ExtensionMode.Default"> <option value="default">
Use default ({{simpleDefaultSettings.enable}}) Use default ({{simpleDefaultSettings.enable}})
</option> </option>
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Never Never
</option> </option>
</template> </template>
<option :value="ExtensionMode.FullScreen"> <option value="fs">
Fullscreen only Fullscreen only
</option> </option>
<option :value="ExtensionMode.Theater"> <option value="theater">
Fullscreen and theater mode Fullscreen and theater mode
</option> </option>
<option :value="ExtensionMode.All"> <option value="enabled">
Always Always
</option> </option>
</select> </select>
@ -63,25 +63,25 @@
(Site uses advanced settings) (Site uses advanced settings)
</option> </option>
<template v-if="isDefaultConfiguration"> <template v-if="isDefaultConfiguration">
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Disabled by default Disabled by default
</option> </option>
</template> </template>
<template v-else> <template v-else>
<option :value="ExtensionMode.Default"> <option value="default">
Use default ({{simpleDefaultSettings.enableAard}}) Use default ({{simpleDefaultSettings.enableAard}})
</option> </option>
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Never Never
</option> </option>
</template> </template>
<option :value="ExtensionMode.FullScreen"> <option value="fs">
Fullscreen only Fullscreen only
</option> </option>
<option :value="ExtensionMode.Theater"> <option value="theater">
Fullscreen and theater mode Fullscreen and theater mode
</option> </option>
<option :value="ExtensionMode.All"> <option value="enabled">
Always Always
</option> </option>
</select> </select>
@ -106,25 +106,25 @@
(Site uses advanced settings) (Site uses advanced settings)
</option> </option>
<template v-if="isDefaultConfiguration"> <template v-if="isDefaultConfiguration">
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Disabled by default Disabled by default
</option> </option>
</template> </template>
<template v-else> <template v-else>
<option :value="ExtensionMode.Default"> <option value="default">
Use default ({{simpleDefaultSettings.enableKeyboard}}) Use default ({{simpleDefaultSettings.enableKeyboard}})
</option> </option>
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Never Never
</option> </option>
</template> </template>
<option :value="ExtensionMode.FullScreen"> <option value="fs">
Fullscreen only Fullscreen only
</option> </option>
<option :value="ExtensionMode.Theater"> <option value="theater">
Fullscreen and theater mode Fullscreen and theater mode
</option> </option>
<option :value="ExtensionMode.All"> <option value="enabled">
Always Always
</option> </option>
</select> </select>
@ -143,26 +143,23 @@
@click="setExtensionMode('enableUI', $event)" @click="setExtensionMode('enableUI', $event)"
> >
<template v-if="isDefaultConfiguration"> <template v-if="isDefaultConfiguration">
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Disabled by default Disabled by default
</option> </option>
</template> </template>
<template v-else> <template v-else>
<option :value="ExtensionMode.Default"> <option value="default">
Use default ({{simpleDefaultSettings.enableUI}}) Use default ({{simpleDefaultSettings.enableUI}})
</option> </option>
<option :value="ExtensionMode.Disabled"> <option value="disabled">
Never Never
</option> </option>
</template> </template>
<option :value="ExtensionMode.FullScreen"> <option value="fs">
Fullscreen only Fullscreen only
</option> </option>
<option :value="ExtensionMode.Theater"> <option value="theater">
Fullscreen and theater Always where possible
</option>
<option :value="ExtensionMode.Theater">
Where possible
</option> </option>
</select> </select>
</div> </div>
@ -299,7 +296,7 @@
v-if="!isDefaultConfiguration" v-if="!isDefaultConfiguration"
:value="CropModePersistence.Default" :value="CropModePersistence.Default"
> >
Use default ({{defaultPersistenceLabel}}) Use default ({{defaultPersistanceLabel}})
</option> </option>
<option :value="CropModePersistence.Disabled">Disabled</option> <option :value="CropModePersistence.Disabled">Disabled</option>
<option :value="CropModePersistence.UntilPageReload">Until page reload</option> <option :value="CropModePersistence.UntilPageReload">Until page reload</option>
@ -309,14 +306,6 @@
</div> </div>
</div> </div>
</div> </div>
<pre>
SITE SETTINGS: raw
{{JSON.stringify(siteSettings.raw)}}
SITE SETTINGS: data
{{JSON.stringify(siteSettings.data)}}
</pre>
</div> </div>
</template> </template>
@ -401,7 +390,7 @@ export default defineComponent({
siteDefaultCropPersistence() { siteDefaultCropPersistence() {
return this.siteSettings.raw?.persistCSA ?? undefined; return this.siteSettings.raw?.persistCSA ?? undefined;
}, },
defaultPersistenceLabel() { defaultPersistanceLabel() {
switch (this.siteSettings.defaultSettings.persistCSA) { switch (this.siteSettings.defaultSettings.persistCSA) {
case CropModePersistence.CurrentSession: case CropModePersistence.CurrentSession:
return 'current session'; return 'current session';
@ -423,7 +412,7 @@ export default defineComponent({
/** /**
* Compiles our extension settings into more user-friendly options * Compiles our extension settings into more user-friendly options
*/ */
compileSimpleSettings(component, getFor = 'site'): ExtensionMode { compileSimpleSettings(component, getFor = 'site') {
let settingsData; let settingsData;
switch (getFor) { switch (getFor) {
@ -438,27 +427,86 @@ export default defineComponent({
break; break;
} }
console.log('compiling simple settings;', settingsData, 'original settings:', this.siteSettings)
return settingsData?.[component]; try {
if (
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Disabled
&& settingsData?.[component]?.fullscreen === ExtensionMode.Disabled
) {
return 'disabled';
}
if (
( settingsData?.[component]?.normal === ExtensionMode.Default || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Default
&& settingsData?.[component]?.fullscreen === ExtensionMode.Default
) {
// console.log(
// component, 'is set to default because:\n',
// `\nsettingsData[${component}].normal: ${settingsData?.[component]?.normal} || component is enableUI?`, component,
// `\nsettingsData[${component}].theater: ${settingsData?.[component]?.normal}`,
// `\nsettingsData[${component}].fullscreen: ${settingsData?.[component]?.normal}`,
// `\n\n(expected values:`, ExtensionMode.Default
// )
return 'default';
}
if (
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Disabled
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
) {
return 'fs';
}
if (
( settingsData?.[component]?.normal === ExtensionMode.Disabled || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Enabled
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
) {
return 'theater';
}
if (
( settingsData?.[component]?.normal === ExtensionMode.Enabled || component === 'enableUI')
&& settingsData?.[component]?.theater === ExtensionMode.Enabled
&& settingsData?.[component]?.fullscreen === ExtensionMode.Enabled
) {
return 'enabled';
}
return 'complex';
} catch (e) {
return 'loading';
}
}, },
getDefaultOptionLabel(component) { getDefaultOptionLabel(component) {
const componentValue: ExtensionMode = this.compileSimpleSettings(component, 'default'); const componentValue = this.compileSimpleSettings(component, 'default');
if (componentValue === 'loading') {
return componentValue;
}
if (component === 'enableUI') {
switch (componentValue) {
case 'fs':
return 'fullscreen only';
case 'enabled':
case 'theater':
return 'where possible';
case 'disabled':
return 'disabled';
}
}
switch (componentValue) { switch (componentValue) {
case ExtensionMode.Disabled: case 'fs':
return 'disabled'; return 'Fullscreen only';
case ExtensionMode.Default: case 'theater':
return 'default'; return 'theater & FS';
case ExtensionMode.FullScreen: case 'enabled':
return 'fullscreen only';
case ExtensionMode.Theater:
return 'fullscreen & theater';
case ExtensionMode.All:
return 'always'; return 'always';
default: case 'disabled':
return '<Invalid value>'; return 'disabled';
case 'default':
return 'complex'
} }
}, },
@ -534,7 +582,7 @@ export default defineComponent({
this._computedWatchers?.siteDefaultStretch?.run(); this._computedWatchers?.siteDefaultStretch?.run();
this._computedWatchers?.siteDefaultAlignment?.run(); this._computedWatchers?.siteDefaultAlignment?.run();
this._computedWatchers?.siteDefaultCropPersistence?.run(); this._computedWatchers?.siteDefaultCropPersistence?.run();
this._computedWatchers?.defaultPersistenceLabel?.run(); this._computedWatchers?.defaultPersistanceLabel?.run();
this.$nextTick( () => this.$forceUpdate()); this.$nextTick( () => this.$forceUpdate());
}, },
@ -547,15 +595,10 @@ export default defineComponent({
setExtensionMode(component, event) { setExtensionMode(component, event) {
const option = event.target.value; const option = event.target.value;
console.log('set extension mode - we received option', option, 'for component', component);
return;
if (option === 'complex') { if (option === 'complex') {
return; return;
} }
if (component === 'enable' && !this.isDefaultConfiguration) { if (component === 'enable' && !this.isDefaultConfiguration) {
this.setExtensionMode('enableAard', event); this.setExtensionMode('enableAard', event);
this.setExtensionMode('enableKeyboard', event); this.setExtensionMode('enableKeyboard', event);
@ -568,6 +611,41 @@ export default defineComponent({
} }
} }
if (option === 'default') {
return this.siteSettings.set(component, {
normal: ExtensionMode.Default,
theater: ExtensionMode.Default,
fullscreen: ExtensionMode.Default
});
}
if (option === 'disabled') {
return this.siteSettings.set(component, {
normal: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
fullscreen: ExtensionMode.Disabled
});
}
if (option === 'enabled') {
return this.siteSettings.set(component, {
normal: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
fullscreen: ExtensionMode.Enabled
});
}
if (option === 'theater') {
return this.siteSettings.set(component, {
normal: ExtensionMode.Disabled,
theater: ExtensionMode.Enabled,
fullscreen: ExtensionMode.Enabled
});
}
if (option === 'fs') {
return this.siteSettings.set(component, {
normal: ExtensionMode.Disabled,
theater: ExtensionMode.Disabled,
fullscreen: ExtensionMode.Enabled
});
}
} }
} }

View File

@ -1,4 +1,4 @@
@import '@src/main.css'; /** postcss processor doesn't support aliases */ @import '../../../../main.css'; /** postcss processor doesn't support aliases */
.keyboard-shortcut-row { .keyboard-shortcut-row {
@apply py-2 px-4 @apply py-2 px-4

View File

@ -30,7 +30,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import KeyboardShortcutParserMixin from '@ui/utils/mixins/KeyboardShortcutParserMixin.vue'; import KeyboardShortcutParserMixin from '../../../../csui/src/utils/KeyboardShortcutParserMixin';
import EditShortcutButton from './EditShortcutButton.vue'; import EditShortcutButton from './EditShortcutButton.vue';
export default defineComponent({ export default defineComponent({

View File

@ -155,6 +155,7 @@ import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
import StretchType from '@src/common/enums/StretchType.enum'; import StretchType from '@src/common/enums/StretchType.enum';
import NewAspectRatioForm from './Components/NewAspectRatioForm.vue'; import NewAspectRatioForm from './Components/NewAspectRatioForm.vue';
import BrowserDetect from '@src/ext/conf/BrowserDetect';
import CropOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/CropOptionsPanel.vue' import CropOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/CropOptionsPanel.vue'
import StretchOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/StretchOptionsPanel.vue' import StretchOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/StretchOptionsPanel.vue'
import ZoomOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/ZoomOptionsPanel.vue' import ZoomOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/ZoomOptionsPanel.vue'

View File

@ -65,7 +65,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import BrowserDetect from '@src/ext/conf/BrowserDetect'; import BrowserDetect from '@src/ext/conf/BrowserDetect';
import SupportLevelIndicator from '@components/common/SupportLevelIndicator.vue'; import SupportLevelIndicator from '@components/common/components/SupportLevelIndicator.vue';
export default defineComponent({ export default defineComponent({
@ -95,7 +95,7 @@ export default defineComponent({
}) })
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>
@import '@src/main.css'; /** postcss processor doesn't support aliases */ @import '../../../main.css'; /** postcss processor doesn't support aliases */
.open-settings-popup { .open-settings-popup {
width: 16rem; width: 16rem;

View File

@ -76,23 +76,30 @@
<div class="flex flex-col panel-content"> <div class="flex flex-col panel-content">
<!-- Panel section --> <!-- Panel section -->
<VideoSettings v-if="selectedTab === 'video-settings'" <VideoSettings
v-if="selectedTab === 'video-settings'"
:settings="settings" :settings="settings"
:siteSettings="siteSettings" :siteSettings="siteSettings"
:eventBus="eventBus" :eventBus="eventBus"
:site="site" :site="site"
></VideoSettings> ></VideoSettings>
<template v-if="settings && selectedTab === 'site-extension-settings'" > <template
v-if="settings && selectedTab === 'site-extension-settings'"
>
<h3>Settings for {{site?.host}}</h3> <h3>Settings for {{site?.host}}</h3>
<SiteExtensionSettings <SiteExtensionSettings
:settings="settings" :settings="settings"
:siteSettings="siteSettings" :siteSettings="siteSettings"
:isDefaultConfiguration="false" :isDefaultConfiguration="false"
></SiteExtensionSettings> ></SiteExtensionSettings>
<pre>{{JSON.stringify(siteSettings.data, null, 2)}}</pre>
</template> </template>
<template v-if="settings && selectedTab === 'embedded-extension-settings'" > <template
v-if="settings && selectedTab === 'embedded-extension-settings'"
>
<h3>Settings for embedded sites</h3> <h3>Settings for embedded sites</h3>
<FrameSiteSettings <FrameSiteSettings
:parentHost="site?.host" :parentHost="site?.host"
@ -102,7 +109,9 @@
</template> </template>
<template v-if="settings && selectedTab === 'default-extension-settings'" > <template
v-if="settings && selectedTab === 'default-extension-settings'"
>
<h3>Default settings</h3> <h3>Default settings</h3>
<SiteExtensionSettings <SiteExtensionSettings
:settings="settings" :settings="settings"
@ -116,7 +125,6 @@
:settings="settings" :settings="settings"
:enableSettingsEditor="true" :enableSettingsEditor="true"
></OtherSiteSettings> ></OtherSiteSettings>
<AutodetectionSettings <AutodetectionSettings
v-if="selectedTab === 'autodetectionSettings'" v-if="selectedTab === 'autodetectionSettings'"
:settings="settings" :settings="settings"
@ -160,17 +168,12 @@
> >
</PlayerDetectionPanel> </PlayerDetectionPanel>
<ImportExportSettings v-if="selectedTab === 'import-export-settings'" <DebugPanel
:settings="settings"
>
</ImportExportSettings>
<Debugging
v-if="selectedTab === 'debugging'" v-if="selectedTab === 'debugging'"
:settings="settings" :settings="settings"
:eventBus="eventBus" :eventBus="eventBus"
:site="site" :site="site"
></Debugging> ></DebugPanel>
<!-- <ResetBackupPanel <!-- <ResetBackupPanel
v-if="selectedTab === 'resetBackup'" v-if="selectedTab === 'resetBackup'"
@ -184,24 +187,24 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import VideoSettings from '@components/segments/VideoSettings/VideoSettings.vue'; import VideoSettings from '@components/VideoSettings/VideoSettings.vue';
import OtherSiteSettings from '@components/segments/ExtensionSettings/Panels/OtherSiteSettings.vue'; import OtherSiteSettings from '@components/ExtensionSettings/Panels/OtherSiteSettings.vue';
import AutodetectionSettings from '@components/segments/AutodetectionSettings/AutodetectionSettings.vue'; import AutodetectionSettings from '@components/AutodetectionSettings/AutodetectionSettings.vue';
import UISettings from '@components/segments/UISettings/UISettings.vue'; import UISettings from '@components/UISettings/UISettings.vue';
import KeyboardShortcutSettings from '@components/segments/KeyboardShortcuts/KeyboardShortcutSettings.vue'; import KeyboardShortcutSettings from '@components/KeyboardShortcuts/KeyboardShortcutSettings.vue';
import SiteExtensionSettings from '@components/segments/ExtensionSettings/Panels/SiteExtensionSettings.vue'; import SiteExtensionSettings from '@components/ExtensionSettings/Panels/SiteExtensionSettings.vue';
import FrameSiteSettings from '@components/segments/ExtensionSettings/Panels/FrameSiteSettings.vue'; import FrameSiteSettings from '@components/ExtensionSettings/Panels/FrameSiteSettings.vue';
import Debugging from '@components/segments/Debugging/Debugging.vue';
import ImportExportSettings from '@components/segments/ImportExportSettings/ImportExportSettings.vue';
import WhatsNew from '@components/segments/ExtensionInfo/WhatsNew.vue'; import WhatsNew from '@components/ExtensionInfo/WhatsNew.vue';
import About from '@components/segments/ExtensionInfo/About.vue'; import About from '@components/ExtensionInfo/About.vue';
// to replace: import DebugPanel from '../../csui/src/PlayerUiPanels/DebugPanel.vue'
import PlayerDetectionPanel from '../../csui/src/PlayerUiPanels/PlayerDetectionPanel.vue' import PlayerDetectionPanel from '../../csui/src/PlayerUiPanels/PlayerDetectionPanel.vue'
import BrowserDetect from '../../ext/conf/BrowserDetect'
// not component: import ChangelogPanel from '../../csui/src/PlayerUiPanels/ChangelogPanel.vue'
import BrowserDetect from '@src/ext/conf/BrowserDetect' import AboutPanel from '@csui/src/PlayerUiPanels/AboutPanel.vue'
import ResetBackupPanel from '../../csui/src/PlayerUiPanels/ResetBackupPanel.vue'
import SupportLevelIndicator from '@csui/src/components/SupportLevelIndicator.vue'
const AVAILABLE_TABS = { const AVAILABLE_TABS = {
@ -224,7 +227,6 @@ const AVAILABLE_TABS = {
'playerDetection': {id: 'playerDetection', label: 'Player detection', icon: 'television-play'}, 'playerDetection': {id: 'playerDetection', label: 'Player detection', icon: 'television-play'},
'changelog': {id: 'changelog', label: 'What\'s new', icon: 'alert-decagram' }, 'changelog': {id: 'changelog', label: 'What\'s new', icon: 'alert-decagram' },
'about': {id: 'about', label: 'About', icon: 'information-outline'}, 'about': {id: 'about', label: 'About', icon: 'information-outline'},
'import-export-settings': { id: 'import-export-settings', label: 'Import & export settings', icon: 'file-export-outline'},
'debugging': {id: 'debugging', label: 'Debugging', icon: 'bug-outline', hidden: true} 'debugging': {id: 'debugging', label: 'Debugging', icon: 'bug-outline', hidden: true}
}; };
@ -236,7 +238,6 @@ const TAB_LOADOUT = {
'keyboardShortcuts', 'keyboardShortcuts',
'changelog', 'changelog',
'about', 'about',
'import-export-settings',
'debugging', 'debugging',
], ],
'popup': [ 'popup': [
@ -256,13 +257,16 @@ export default defineComponent({
UISettings, UISettings,
SiteExtensionSettings, SiteExtensionSettings,
FrameSiteSettings, FrameSiteSettings,
ImportExportSettings,
Debugging,
WhatsNew, WhatsNew,
About, About,
PlayerDetectionPanel, PlayerDetectionPanel,
DebugPanel,
ChangelogPanel,
AboutPanel,
SupportLevelIndicator,
ResetBackupPanel,
}, },
mixins: [], mixins: [],
data() { data() {
@ -301,9 +305,6 @@ export default defineComponent({
watch: { watch: {
'initialPath': function (newVal) { 'initialPath': function (newVal) {
this.setInitialPath(newVal); this.setInitialPath(newVal);
},
'site': function (newVal) {
this.updateSite(newVal);
} }
}, },
computed: { computed: {
@ -324,9 +325,7 @@ export default defineComponent({
changelogTab.highlight = !this.settings.active?.whatsNewChecked; changelogTab.highlight = !this.settings.active?.whatsNewChecked;
} }
if (this.site) { this.siteSettings = this.settings.getSiteSettings({site: this.site});
this.siteSettings = this.settings.getSiteSettings({site: this.site});
}
this.eventBus?.subscribe( this.eventBus?.subscribe(
'uw-show-ui', 'uw-show-ui',
{ {
@ -345,13 +344,6 @@ export default defineComponent({
this.eventBus?.unsubscribeAll(this); this.eventBus?.unsubscribeAll(this);
}, },
methods: { methods: {
/**
* Regenerates this.siteSettings
*/
updateSite(newSite: {host: string, hostnames: string[],}) {
this.siteSettings = this.settings.getSiteSettings({site: newSite.host});
this.$nextTick( () => this.$forceUpdate());
},
/** /**
* Gets URL of the browser settings page (i think?) * Gets URL of the browser settings page (i think?)
*/ */
@ -374,13 +366,11 @@ export default defineComponent({
console.log('setting initial path:', this.initialPath) console.log('setting initial path:', this.initialPath)
if (path && path.length) { if (path && path.length) {
this.selectedTab = path[0]; this.selectedTab = path[0];
this.$emit('debugStatusChanged', this.selectedTab === 'debugging');
} }
}, },
selectTab(tab) { selectTab(tab) {
console.log("Selecting tab", tab); console.log("Selecting tab", tab);
this.selectedTab = tab; this.selectedTab = tab;
this.$emit('debugStatusChanged', tab === 'debugging');
window.location.hash = `#${this.role}/${this.selectedTab}`; window.location.hash = `#${this.role}/${this.selectedTab}`;
}, },
setPreventClose(bool) { setPreventClose(bool) {
@ -399,7 +389,7 @@ export default defineComponent({
}); });
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>
@import '@src/main.css'; /** postcss processor doesn't support aliases */ @import '../../main.css'; /** postcss processor doesn't support aliases */
.settings-categories { .settings-categories {
@apply @apply

View File

@ -113,12 +113,12 @@ import { defineComponent } from 'vue';
import BrowserDetect from '@src/ext/conf/BrowserDetect'; import BrowserDetect from '@src/ext/conf/BrowserDetect';
import ShortcutButton from './components/ShortcutButton.vue'; import ShortcutButton from './components/ShortcutButton.vue';
import CommsMixin from '@ui/utils/mixins/CommsMixin.vue'; import CommsMixin from '@components/common/mixins/CommsMixin.vue';
import KeyboardShortcutParserMixin from '@ui/utils/mixins/KeyboardShortcutParserMixin.vue'; import KeyboardShortcutParserMixin from '@components/common/mixins/KeyboardShortcutParserMixin.vue';
import alignmentIndicatorSvg from '!!raw-loader!@ui/res/img/alignment-indicators.svg?raw'; import alignmentIndicatorSvg from '!!raw-loader!@ui/res/img/alignment-indicators.svg?raw';
import {setupVideoAlignmentIndicatorInteraction, setVideoAlignmentIndicatorState} from '@ui/utils/video-alignment-indicator-handling'; import {setupVideoAlignmentIndicatorInteraction, setVideoAlignmentIndicatorState} from '@ui/utils/video-alignment-indicator-handling.ts';
import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum'; import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -227,7 +227,7 @@ export default defineComponent({
}); });
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>
@import '@src/main.css'; @import '../../../main.css';
.button-container { .button-container {
@apply w-full pt-2 @apply w-full pt-2

View File

@ -1,109 +0,0 @@
<template>
<div v-if="popupVisible" class="popup">
<div class="popup-window">
<div
class="header"
:class="{
'danger': dialogType === 'danger',
'warning': dialogType === 'warning',
'info': dialogType === 'info',
'normal': dialogType === 'normal'
}"
>
{{ dialogText || 'Confirm action' }}
</div>
<div class="body">
{{ dialogText || 'Are you sure you want to do that?' }}
</div>
<div class="footer w-full flex row gap-2 justify-end">
<button
class="button confirm"
:class="{
'danger': dialogType === 'danger',
'warning': dialogType === 'warning',
'info': dialogType === 'info',
'normal': dialogType === 'normal'
}"
@click="confirmAction"
>
{{ confirmText || 'Confirm' }}
</button>
<button class="button" @click="popupVisible = false">{{ cancelText || 'Cancel' }}</button>
</div>
</div>
</div>
<button
:class="[
{
'danger': dialogType === 'danger',
'warning': dialogType === 'warning',
},
btnClass
]"
@click="popupVisible = true"
>
<slot></slot>
</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
data() {
return {
popupVisible: false,
}
},
props: [
'btnClass',
'dialogTitle',
'dialogText',
'confirmText',
'cancelText',
'dialogType'
],
methods: {
confirmAction() {
this.popupVisible = false;
this.$emit('onConfirmed');
}
}
});
</script>
<style lang="postcss" scoped>
.popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
backdrop-filter: saturate(50%) brightness(50%) blur(1rem);
z-index: 99999;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.header {
font-size: 1.33rem;
color: #fff;
&.danger {
color: rgb(251, 107, 63);
font-weight: bold;
}
}
.body {
min-height: 5rem;
}
}
</style>

View File

@ -1,120 +0,0 @@
<template>
<div class="flex flex-row w-full">
<button
v-if="editorMode === 'tree'"
@click="() => setEditorMode('text')"
>
Edit as text
</button>
<button
v-else
@click="() => setEditorMode('tree')"
>
Show as tree
</button>
</div>
<div ref="refContainer" class="w-full h-full"></div>
</template>
<script lang="ts">
import { createJSONEditor, Mode } from "vanilla-jsoneditor";
export default {
props: {
modelValue: Object // v-model binding
},
emits: ["update:modelValue"],
data() {
return {
refContainer: null,
editor: null,
editorMode: Mode.tree,
};
},
watch: {
modelValue: {
deep: true,
handler(newValue) {
if (this.editor) {
this.editor.updateProps({ content: { json: newValue || {} } });
}
}
}
},
mounted() {
if (this.$refs.refContainer) {
this.editor = createJSONEditor({
target: this.$refs.refContainer,
props: {
mode: Mode.tree,
content: { json: this.modelValue || {} },
onChange: (updatedContent) => {
this.$emit("update:modelValue", updatedContent.json);
},
onRenderContextMenu: false,
navigationBar: false,
statusBar: false,
mainMenuBar: false
}
});
}
},
methods: {
setEditorMode(mode) {
this.editorMode = mode
this.editor.updateProps({ mode });
}
},
beforeUnmount() {
if (this.editor) {
this.editor.destroy();
this.editor = null;
}
}
};
</script>
<style lang="postcss">
:root {
--jse-panel-background: #111;
--jse-background-color: #000;
--jse-text-color: #ccc;
--jse-key-color: #8c8bef;
--jse-selection-background-color: rgba(255, 171, 102, 0.177);
--jse-context-menu-pointer-background: rgba(255, 171, 102, 0.177);
--jse-delimiter-color: #fa6;
--jse-value-color-boolean: rgb(132, 132, 137);
}
.jse-boolean-toggle[role="checkbox"] {
&[aria-checked="true"] {
color: rgb(218, 244, 238);
}
&[aria-checked="false"] {
border: 0px transparent;
outline: 0px transparent;
color: rgb(241, 107, 25);
background-color: rgb(241, 107, 25);
position: relative;
&:after {
position: absolute;
content: '×';
top: 0;
left: 0;
width: 100%;
height: 100%;
color: black;
text-align: center;
font-weight: bold;
}
}
}
</style>

View File

@ -1,111 +0,0 @@
<template>
<div
v-if="title"
class="popup-overlay"
:class="{'dim': dimOverlay}"
>
<div class="popup-content p-4 border border-stone-700 max-h-[90dvh] max-w-[90dvw]" >
<div class="h-full flex flex-col">
<div v-if="title" class="header grow-0 shrink-0" :class="type">
<h3 class="mb-4 mt-0">{{title}}</h3>
</div>
<p v-if="message">
{{ message }}
</p>
<div v-else class="grow shrink overflow-auto">
<slot></slot>
</div>
<div class="grow-0 shrink-0 flex row gap-2 justify-end w-full">
<button
class="primary"
v-if="confirmButtonText"
:class="type"
@click="$emit('onConfirm')"
>
{{ confirmButtonText }}
</button>
<button
:class="type"
@click="$emit('onCancel')"
>
{{ cancelButtonText }}
</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
props: {
// onConfirm: {
// type: Function,
// required: false
// },
// onCancel: {
// type: Function,
// default: () => this.$emit('close')
// },
title: {
type: String,
required: false
},
message: {
type: String,
required: false, // technically prolly should be true, but we don't have to guard against skill issue
},
dimOverlay: {
type: Boolean,
default: true
},
type: {
type: String,
default: 'info'
},
confirmButtonText: {
type: String,
required: false,
},
cancelButtonText: {
type: String,
default: 'Cancel'
}
}
}
</script>
<style lang="postcss" scoped>
.popup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
backdrop-filter: saturate(50%) brightness(50%) blur(1rem);
z-index: 99999;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.header {
font-size: 1.33rem;
color: #fff;
&.danger {
color: rgb(251, 107, 63);
font-weight: bold;
}
}
.body {
min-height: 5rem;
}
}
</style>

View File

@ -1,79 +0,0 @@
<template>
<button
class="button drop-zone"
:class="[{'drag-over': isDragOver}, className]"
@click="onClick"
@dragover="isDragOver = true"
@dragleave="isDragOver = false"
@drop="onDrop"
>
<slot></slot>
<input type="file" ref="fileInput" @change="onFileChange" accept=".json" />
</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
className: {
type: String,
default: ""
}
},
data() {
return {
isDragOver: false
};
},
methods: {
onClick() {
this.$refs.fileInput.click();
},
onDrop(event) {
event.preventDefault();
this.isDragOver = false;
if (event.dataTransfer.files.length > 0) {
this.handleFile(event.dataTransfer.files[0]);
}
},
onFileChange(event) {
if (event.target.files.length > 0) {
this.handleFile(event.target.files[0]);
}
},
handleFile(file) {
if (file.type !== "application/json") {
this.$emit('error', 'NOT_JSON_FILE');
return;
}
const reader = new FileReader();
reader.onload = async (event) => {
try {
const importedData = JSON.parse(event.target.result as string);
this.$emit('importedJson', importedData);
} catch (error) {
this.$emit('error', 'INVALID_JSON');
}
};
reader.readAsText(file);
},
}
});
</script>
<style lang="postcss" scoped>
input {
display: none;
}
.drop-zone {
&.drag-over {
background-color: #fa6;
color: black;
}
}
</style>

View File

@ -62,7 +62,7 @@ export default defineComponent({
}, },
computed: { computed: {
computedSiteSupportLevel() { computedSiteSupportLevel() {
return this.siteSupportLevel ?? (this.siteSettings ? this.siteSettings.data.type ?? 'no-support' : 'waiting'); return this.siteSupportLevel ?? this.siteSettings ? this.siteSettings.data.type || 'no-support' : 'waiting';
} }
} }
@ -70,7 +70,7 @@ export default defineComponent({
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>
@import '@src/main.css'; /** postcss processor doesn't support aliases */ @import '../../../../main.css'; /** postcss processor doesn't support aliases */
.site-support { .site-support {
@apply inline-flex flex-row items-center gap-1 rounded-[0.5rem] relative; @apply inline-flex flex-row items-center gap-1 rounded-[0.5rem] relative;

View File

@ -1,243 +0,0 @@
<template>
<h2 class="text-[1.75em]">Debugging</h2>
<div class="w-full grid grid-cols-1 min-[1200px]:grid-cols-2 min-[1920px]:grid-cols-3 max-w-[2300px] gap-8">
<div class="grow shrink">
<h3 class="mb-4">Settings editor</h3>
<div class="flex flex-row w-full">
<div class="flex flex-row items-center gap-2">
<div>Enable save button:</div>
<input v-model="allowSettingsEditing" type="checkbox">
</div>
<div class="grow">
</div>
<div>
<div v-if="editorSaveFinished">Settings saved ...</div>
<button v-else
class="danger"
:class="{'opacity-50 select-none hover:border-stone-500 hover:text-stone-300': !allowSettingsEditing}"
:disabled="!allowSettingsEditing"
@click="() => saveSettingsChanges()"
>
Save
</button>
</div>
<button @click="resetSettingsEditor">
Cancel
</button>
</div>
<div>
<JsonEditor
v-model="settingsJson"
>
</JsonEditor>
</div>
</div>
<div class="grow shrink">
<h3 class="mb-4">Snapshots</h3>
<div class="flex flex-col gap-2">
<div
v-for="(snapshot, index) of settingsSnapshots" :key="snapshot.createdAt"
class="border border-stone-800 p-2"
>
<small>Created at: {{new Date(snapshot.createdAt).toISOString()}}</small>
<div class="flex flex-row">
<div class="grow font-bold">
{{snapshot.label ?? '<no label>'}}
</div>
<div class="mr-8">v. {{snapshot.settings.version ?? '<unknown ver.>'}}</div>
<div v-if="settings.isAutomatic">(auto)</div>
<div v-if="settings.isAutomatic && settings.isProtected">(protected)</div>
<div v-if="settings.default">(default)</div>
</div>
<div class="flex flex-row w-full justify-end mt-2">
<button class="flex flex-col" @click="() => testMigration(index)">Test migration</button>
<button class="flex flex-col" @click="() => loadFromSnapshot(index)">Load snapshot and migrate<br/><small>normal migration</small></button>
<button class="flex flex-col" @click="() => loadFromSnapshot(index, true)">Load &amp; migrate<br/><small>without creating snapshot</small></button>
<button class="flex flex-col" @click="() => markDefaultSnapshot(index)"><template v-if="settings.isDefault">Revoke default</template><template v-else>Make default</template></button>
<button class="flex flex-col" v-if="settings.isAutomatic" @click="() => toggleSnapshotProtection(index)">Toggle protection</button>
<button class="flex flex-col" @click="() => deleteSnapshot(index)">Delete snapshot</button>
</div>
</div>
</div>
</div>
<div class="grow shrink">
<h3 class="mb-4">Logger configuration</h3>
<JsonEditor
v-model="lastSettings"
></JsonEditor>
<div class="flex flex-row justify-end mt-4 w-full">
<button class="bg-black button" @click="getLoggerSettings()">
Revert
</button>
<button class="button button-primary" @click="saveLoggerSettings()">
Save
</button>
<button class="button button-primary" @click="startLogging()">
Save and start logging
</button>
</div>
</div>
</div>
</template>
<script lang="ts">
import { LogAggregator, BLANK_LOGGER_CONFIG } from '@src/ext/lib/logging/LogAggregator';
import JsonEditor from '@components/common/JsonEditor.vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
JsonEditor
},
data() {
return {
loggerPanelRotation: [{
title: 'DEFORESTATOR 5000',
subtitle: 'Iron Legion\'s finest logging tool'
},{
title: 'Astinus',
subtitle: 'Ultrawidify logging tool'
}, {
title: 'Tracer',
subtitle: "Maybe I'll be Tracer — I'm already printing stack traces."
}, {
title: 'Grûmsh',
subtitle: "He who watches (all the things printed to the console)."
}, {
title: 'Situation Room/The Council',
subtitle: 'We will always be watching.'
}, {
title: 'Isengard Land Management Services',
subtitle: "#log4saruman"
}, {
title: 'Saw Hero',
subtitle: 'Ultrawidify logging tool'
}],
loggerPanel: {
title: 'whoopsie daisy',
subtitle: 'you broke the header choosing script',
pasteConfMode: false,
},
lastSettings: undefined,
parsedSettings: undefined,
currentSettings: undefined,
allowSettingsEditing: false,
editorSaveFinished: false,
settingsJson: {},
settingsSnapshots: []
};
},
props: [
'settings',
'eventBus',
],
created() {
this.loggerPanel = {
...this.loggerPanelRotation[Math.floor(Math.random() * this.loggerPanelRotation.length)],
pasteConfMode: false,
};
this.getLoggerSettings();
},
mounted() {
this.loadSettingsSnapshots();
this.resetSettingsEditor();
this.loadSettingsSnapshots();
},
methods: {
loadDefaultConfig() {
this.lastSettings = JSON.parse(JSON.stringify(BLANK_LOGGER_CONFIG));
},
async getLoggerSettings() {
this.lastSettings = await LogAggregator.getConfig() || BLANK_LOGGER_CONFIG;
},
async saveLoggerSettings() {
console.log('Saving logger settings', this.lastSettings);
await LogAggregator.saveConfig({...this.lastSettings});
console.log('[ok] logger settings saved');
},
async startLogging(){
this.logStringified = undefined;
await LogAggregator.saveConfig({...this.lastSettings});
window.location.reload();
},
/**
* Resets settings to default
*/
resetSettings() {
this.settings.active = JSON.parse(JSON.stringify(this.settings.default));
this.settings.saveWithoutReload();
this.resetSettingsEditor();
},
async saveSettingsChanges() {
if (this.allowSettingsEditing) {
const currentVersion = this.settings.active?.version;
this.settings.active = this.settingsJson;
if (currentVersion !== this.settingsJson.version) {
await this.settings.save({forcePreserveVersion: true});
return;
} else {
await this.settings.saveWithoutReload();
}
this.resetSettingsEditor();
this.editorSaveFinished = true;
setTimeout(() => {
this.editorSaveFinished = false;
}, 3000);
}
},
resetSettingsEditor() {
this.settingsJson = JSON.parse(JSON.stringify(this.settings?.active ?? {}));
},
//#endregion
//#region settings snapshot management
async loadSettingsSnapshots() {
this.settingsSnapshots = await this.settings.snapshotManager.listSnapshots();
},
async markDefaultSnapshot(index) {
await this.settings.snapshotManager.setDefaultSnapshot(index, !this.settingsSnapshots[index].isDefault);
await this.loadSettingsSnapshots();
this.settings.active.dev.loadFromSnapshot = this.settingsSnapshots[index].isDefault;
this.saveSettingsChanges();
},
async toggleSnapshotProtection(index) {
await this.settings.snapshotManager.setSnapshotAsProtected(index, !this.settingsSnapshots[index].isProtected);
await this.loadSettingsSnapshots();
},
async deleteSnapshot(index) {
await this.settings.snapshotManager.deleteSnapshot(index);
await this.loadSettingsSnapshots();
},
async loadFromSnapshot(snapshotIndex, skipSnapshot?: boolean) {
this.settings.init({snapshot: this.settingsSnapshots[snapshotIndex], skipSnapshot});
},
async testMigration(snapshotIndex) {
this.settings.testMigration(this.settingsSnapshots[snapshotIndex]);
},
}
//#endregion
});
</script>

View File

@ -1,190 +0,0 @@
<template>
<div class="flex flex-col w-full max-w-[960px]">
<h2 text="text-[1.75rem]">Import &amp; export settings</h2>
<!-- Reset options -->
<h3>Import &amp; export</h3>
<p>
Note that settings may contain the sites you visited and used this extension on. If you are being asked to share settings for debugging a bug report
and are concerned with your privacy <small>(which you should)</small>, select partial export and only select the websites relevant to your issue.
</p>
<div class="flex flex-row w-full gap-2">
<UploadJsonFileButton
@importedJson="handleImportedSettings"
@error="handleSettingsImportError"
>
Import settings
</UploadJsonFileButton>
<button @click="exportSettings()">Export settings</button>
<button @click="startPartialExport()">Partial export </button>
</div>
<h3>Reset settings</h3>
<p>Reset settings to this version's default settings. Your custom settings will be lost.</p>
<ConfirmButton
dialogType="danger"
@onConfirmed="resetSettings"
>
Reset settings
</ConfirmButton>
<!--
<div v-if="enableSettingsEditor" class="field">
<div class="label">Show developer options</div>
<input
type="checkbox"
v-model="settings.active.ui.devMode"
@change="settings.saveWithoutReload"
>
</div> -->
</div>
<Popup
v-if="importSettingDialogConfig.visible"
title="Overwrite existing settings?"
message="Importing settings from a file will overwrite existing settings. Continue?"
confirmButtonText="Import settings"
cancelButtonText="Cancel"
@onConfirm="importSettingDialogConfig.confirm"
@onCancel="importSettingDialogConfig.reject"
>
</Popup>
<Popup
v-if="partialExportDialogConfig.visible"
title="Custom settings export"
confirmButtonText="Export"
cancelButtonText="Cancel"
@onConfirm="partialExportDialogConfig.confirm"
@onCancel="partialExportDialogConfig.reject"
>
<p>Export settings for the following websites:</p>
<div class="flex flex-col my-4">
<div v-for="site in userSites" :key="site"
class="border border-stone-700 px-4 py-2 flex row gap-4"
>
<input type="checkbox" v-model="site.selectedForExport" :disabled="site.key === '@global'"> {{site.key}} <SupportLevelIndicator :siteSupportLevel="site.type ?? site.defaultType ?? 'problemo'"></SupportLevelIndicator>
</div>
</div>
</Popup>
</template>
<script lang="ts">
import Popup from '@components/common/Popup.vue';
import ConfirmButton from '@components/common/ConfirmButton.vue';
import UploadJsonFileButton from '@components/common/UploadJsonFileButton.vue';
import { SiteSettingsInterface } from '@src/common/interfaces/SettingsInterface';
import SupportLevelIndicator from '@components/common/SupportLevelIndicator.vue';
import { SiteSupportLevel } from '../../../../common/enums/SiteSupportLevel.enum';
interface ExportSiteData extends SiteSettingsInterface {
key: string,
selectedForExport: boolean;
};
export default {
components: {
Popup,
ConfirmButton,
UploadJsonFileButton,
SupportLevelIndicator,
},
mixins: [],
props: [
'settings',
],
data() {
return {
tab: 'siteSettings',
importSettingDialogConfig: {visible: false},
partialExportDialogConfig: {visible: false},
allowSettingsEditing: false,
editorSaveFinished: false,
settingsJson: {},
settingsSnapshots: []
}
},
computed: {
userSites() {
return this.partialExportDialogConfig.filter(
(x: ExportSiteData) => (x.type ?? x.defaultType) === SiteSupportLevel.UserDefined || (x.type ?? x.defaultType) === SiteSupportLevel.UserModified);
},
},
methods: {
setTab(tab) {
this.tab = tab;
},
//#region settings management
/**
* Exports extension settings into a json file.
*/
exportSettings() {
const settingBlob = new Blob(
[ JSON.stringify(this.settings.active, null, 2) ],
{ type: "application/json"}
);
const url = window.URL.createObjectURL(settingBlob);
const a = document.createElement("a");
a.href = url;
a.download = "ultrawidify-settings.json";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
},
startPartialExport() {
console.log('starting partial export ...');
const sites: ExportSiteData[] = [];
for (const site in this.settings.active.sites) {
sites.push({
...JSON.parse(JSON.stringify(this.settings.active.sites[site])),
key: site,
selectedForExport: true
});
}
this.partialExportDialogConfig = {
visible: true,
sites,
confirm: () => {
console.log('selected sites:', this.partialExportDialogConfig.sites.filter(x => x.selectedForExport));
},
reject: () => {
this.partialExportDialogConfig = {visible: false};
}
}
},
handleImportedSettings(newSettings) {
this.importSettingDialogConfig = {
visible: true,
confirm: () => {
this.settings.active = newSettings;
this.settings.saveWithoutReload();
this.importSettingDialogConfig = {visible: false};
},
reject: () => {
this.importSettingDialogConfig = {visible: false};
}
}
},
handleSettingsImportError(error) {
console.error('Error importing settings:', error);
},
resetSettings() {
this.settings.active = JSON.parse(JSON.stringify(this.settings.default));
this.settings.saveWithoutReload();
},
}
}
</script>

View File

@ -61,7 +61,7 @@
</template> </template>
<script> <script>
import BrowserDetect from '@src/ext/conf/BrowserDetect'; import BrowserDetect from '../../../ext/conf/BrowserDetect';
import { LogAggregator } from '@src/ext/lib/logging/LogAggregator'; import { LogAggregator } from '@src/ext/lib/logging/LogAggregator';
import { ComponentLogger } from '@src/ext/lib/logging/ComponentLogger'; import { ComponentLogger } from '@src/ext/lib/logging/ComponentLogger';

View File

@ -6,10 +6,7 @@
"> ">
<!-- page content --> <!-- page content -->
<div <div class="w-full max-w-[1920px] h-[100dvh] overflow-hidden flex flex-col">
class="w-full h-[100dvh] overflow-hidden flex flex-col"
:class="{'max-w-[1920px]': !isDebugging}"
>
<PopupHead <PopupHead
v-if="role === 'popup' && settings && siteSettings && eventBus" v-if="role === 'popup' && settings && siteSettings && eventBus"
:settings="settings" :settings="settings"
@ -35,7 +32,6 @@
:logger="logger" :logger="logger"
:inPlayer="false" :inPlayer="false"
:site="site" :site="site"
@debugStatusChanged="setDebugStatus"
> >
</SettingsWindowContent> </SettingsWindowContent>
</div> </div>
@ -55,7 +51,7 @@ import EventBus from '@src/ext/lib/EventBus';
import CommsClient, { CommsOrigin } from '@src/ext/lib/comms/CommsClient'; import CommsClient, { CommsOrigin } from '@src/ext/lib/comms/CommsClient';
import {ChromeShittinessMitigations as CSM} from '@src/common/js/ChromeShittinessMitigations'; import {ChromeShittinessMitigations as CSM} from '@src/common/js/ChromeShittinessMitigations';
import PopupHead from '@components/PopupHead.vue'; import PopupHead from '@components/PopupHead/PopupHead.vue';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -74,7 +70,6 @@ export default defineComponent({
settingsInitialized: false, settingsInitialized: false,
role: 'settings', role: 'settings',
initialPath: undefined as string[] | undefined, initialPath: undefined as string[] | undefined,
isDebugging: false,
} }
}, },
async created() { async created() {
@ -92,18 +87,10 @@ export default defineComponent({
await this.setupSettingsPage(); await this.setupSettingsPage();
} }
if (segment !== '#popup') {
// undo that workaround that makes popup work correctly if we aren't in a popup
document.getElementsByTagName('html')[0].setAttribute('style', '');
}
await this.settings.init(); await this.settings.init();
this.settingsInitialized = true; this.settingsInitialized = true;
}, },
methods: { methods: {
setDebugStatus(isDebugging: boolean) {
this.isDebugging = isDebugging;
},
updateConfig() { updateConfig() {
this.settings.init(); this.settings.init();
this.$nextTick( () => this.$forceUpdate()); this.$nextTick( () => this.$forceUpdate());

View File

@ -1,6 +1,6 @@
@font-face { @font-face {
font-family: "Heebo"; font-family: "Heebo";
src: url("@res/fonts/Heebo.ttf") format("truetype-variations"); src: url("../fonts/Heebo.ttf") format("truetype-variations");
font-weight: 100 900; font-weight: 100 900;
font-stretch: 75% 125%; font-stretch: 75% 125%;
font-style: normal; font-style: normal;
@ -9,7 +9,7 @@
@font-face { @font-face {
font-family: "Source Code Pro"; font-family: "Source Code Pro";
src: url("@res/fonts/SourceCodePro.ttf") format("truetype-variations"); src: url("../fonts/SourceCodePro.ttf") format("truetype-variations");
font-weight: 200 900; font-weight: 200 900;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
@ -17,7 +17,7 @@
@font-face { @font-face {
font-family: "Source Code Pro"; font-family: "Source Code Pro";
src: url("@res/fonts/SourceCodePro-Italic.ttf") format("truetype-variations"); src: url("../fonts/SourceCodePro-Italic.ttf") format("truetype-variations");
font-weight: 200 900; font-weight: 200 900;
font-style: italic; font-style: italic;
font-display: swap; font-display: swap;

View File

@ -1,20 +0,0 @@
@import "tailwindcss";
@import "tailwindcss/utilities";
@theme {
--breakpoint-popup: 640px;
--breakpoint-window: 960px;
--color-primary: #ff872c;
--color-primary-50: #ffddbe;
--color-primary-100: #ffcda0;
--color-primary-200: #ffbd83;
--color-primary-300: #ffac66;
--color-primary-400: #ff9a4a;
--color-primary-500: #ff872c;
--color-primary-600: #de7622;
--color-primary-700: #be6518;
--color-primary-800: #9f540e;
--color-primary-900: #824406;
--color-primary-950: #663400;
}

View File

@ -1,281 +0,0 @@
import { CommandInterface } from '@src/common/interfaces/SettingsInterface';
import Settings from '@src/ext/lib/settings/Settings';
const SVG_NS = "http://www.w3.org/2000/svg";
class MenuItemConfig {
textSize: number;
width: number
height: number;
xPad: number;
yPad: number;
constructor(options: {textSize: number, width: number, xPad: number, yPad: number}) {
this.textSize = options.textSize;
this.xPad = options.xPad;
this.yPad = options.yPad;
this.height = options.textSize + 2 * options.yPad;
this.width = options.width;
}
}
class MenuItem {
label: string;
hasHover: boolean;
clickAction: () => void;
parent?: MenuItem;
children?: MenuItem[];
svg: any;
static config: MenuItemConfig;
static fromCommand(command: CommandInterface, executor: (command: string, args?: any) => void, parent?: MenuItem) {
return new MenuItem({
label: command.label,
clickAction: () => {executor(command.action, command.arguments)},
parent: parent
});
}
constructor(data: any) {
this.label = data.label;
this.clickAction = data.clickAction;
this.hasHover = false;
this.children = [] as MenuItem[];
};
private createSvg() {
const group = document.createElementNS(SVG_NS, "g");
const textElem = document.createElementNS(SVG_NS, "text");
textElem.textContent = this.label;
textElem.setAttribute('x', `${MenuItem.config.xPad}`);
textElem.setAttribute('y', `${MenuItem.config.yPad}`);
group.appendChild(textElem);
// Measure text width safely
// const width = textElem.getComputedTextLength() + 2 * hPadding;
// const height = fontSize + 2 * vPadding;
// Background rectangle
const rect = document.createElementNS(SVG_NS, "rect");
rect.setAttribute('x', '0');
rect.setAttribute('y', `0`);
rect.setAttribute('width', `${MenuItem.config.width}`);
rect.setAttribute('height', `${MenuItem.config.height}`);
group.insertBefore(rect, textElem);
this.svg = group;
}
addSubitem(subitem: MenuItem) {
this.children.push(subitem);
if (!subitem.parent) {
subitem.parent = this;
}
}
}
function buildMenu(settings: Settings, executor: (command: string, args?: any) => void) {
const menu = new MenuItem(
{label: 'Ultrawidify'}
);
const topLevelItems: { label: string; submenu?: CommandInterface[]; isZoom?: boolean }[] = [
{ label: "Ultrawidify" },
...(settings.active.commands.crop ? [{ label: "Crop", submenu: settings.active.commands.crop }] : []),
...(settings.active.commands.stretch ? [{ label: "Stretch", submenu: settings.active.commands.stretch }] : []),
...(settings.active.commands.zoom ? [{ label: "Zoom", submenu: settings.active.commands.zoom, isZoom: true }] : []),
{ label: "Alignment", submenu: [] },
{ label: "Extension settings" },
{ label: "Incorrect cropping?" },
{ label: "Report problem" }
];
}
function createMenuItem(label) {
}
export function createSvgMenu(playerEl: HTMLElement, settings: Settings) {
if (!settings) {
console.warn('trying to create menu, but settings are not defined:', settings, 'player el:', playerEl);
return;
}
const style = document.createElement('style');
style.textContent = `
@font-face {
font-family: 'Heebo';
src: url(${chrome.runtime.getURL('/ui/res/fonts/Heebo.tff')}) format('truetype');
}
@font-face {
font-family: 'SourceCodePro';
src: url(${chrome.runtime.getURL('/ui/res/fonts/SourceCodePro.tff')}) format('truetype');
}
#_uw_player_menu { width:100%; height:100%; position:absolute; top:0; left:0; pointer-events:none; }
#_uw_player_menu {
.menu { pointer-events:auto; }
.menu-item text { font-family: 'Heebo'; font-size:14px; fill:#fff; }
.submenu { display:none; }
.menu-item:hover > .submenu { display:block; }
.hover-zone { fill:transparent; pointer-events:auto; }
.menu-item rect { fill:#222; rx:4; }
.menu-item:hover > rect { fill:#444; }
}
`;
const svg = document.createElementNS(SVG_NS, "svg");
svg.setAttribute('id', '_uw_player_menu');
svg.append(style);
playerEl.appendChild(svg);
// shadow.appendChild(svg);
// menuGroup.setAttribute('class', 'menu');
svg.appendChild(menuGroup);
const fontSize = 14;
const vPadding = fontSize * 0.4;
const hPadding = fontSize * 0.6;
let currentY = 0;
// --- Top level items ---
const topLevelItems: { label: string; submenu?: CommandInterface[]; isZoom?: boolean }[] = [
{ label: "Ultrawidify" },
...(settings.active.commands.crop ? [{ label: "Crop", submenu: settings.active.commands.crop }] : []),
...(settings.active.commands.stretch ? [{ label: "Stretch", submenu: settings.active.commands.stretch }] : []),
...(settings.active.commands.zoom ? [{ label: "Zoom", submenu: settings.active.commands.zoom, isZoom: true }] : []),
{ label: "Alignment", submenu: [] },
{ label: "Extension settings" },
{ label: "Incorrect cropping?" },
{ label: "Report problem" }
];
topLevelItems.forEach(item => {
const itemGroup = document.createElementNS(SVG_NS, "g");
itemGroup.setAttribute('class','menu-item');
menuGroup.appendChild(itemGroup);
// Text element
const textElem = document.createElementNS(SVG_NS, "text");
textElem.textContent = item.label;
textElem.setAttribute('x', `${hPadding}`);
textElem.setAttribute('y', `${currentY + fontSize}`);
itemGroup.appendChild(textElem);
// Measure text width safely
const width = textElem.getComputedTextLength() + 2 * hPadding;
const height = fontSize + 2 * vPadding;
// Background rectangle
const rect = document.createElementNS(SVG_NS, "rect");
rect.setAttribute('x', '0');
rect.setAttribute('y', `${currentY}`);
rect.setAttribute('width', `${width}`);
rect.setAttribute('height', `${height}`);
itemGroup.insertBefore(rect, textElem);
// --- Submenu ---
if(item.submenu && item.submenu.length > 0) {
const submenuGroup = document.createElementNS(SVG_NS, "g");
submenuGroup.setAttribute('class','submenu');
itemGroup.appendChild(submenuGroup);
let subY = currentY;
item.submenu.forEach(cmd => {
const subGroup = document.createElementNS(SVG_NS, "g");
subGroup.setAttribute('class','menu-item');
submenuGroup.appendChild(subGroup);
// Submenu text
const subText = document.createElementNS(SVG_NS, "text");
subText.textContent = cmd.label + (cmd.shortcut ? ` (${cmd.shortcut.key})` : '');
subText.setAttribute('x', `${width + hPadding}`);
subText.setAttribute('y', `${subY + fontSize}`);
subGroup.appendChild(subText);
const subWidth = subText.getComputedTextLength() + 2*hPadding;
const subHeight = fontSize + 2*vPadding;
const subRect = document.createElementNS(SVG_NS, "rect");
subRect.setAttribute('x', `${width}`);
subRect.setAttribute('y', `${subY}`);
subRect.setAttribute('width', `${subWidth}`);
subRect.setAttribute('height', `${subHeight}`);
subGroup.insertBefore(subRect, subText);
subY += subHeight;
});
// Zoom slider
if(item.isZoom) {
const sliderGroup = document.createElementNS(SVG_NS, "foreignObject");
sliderGroup.setAttribute('x', `${width}`);
sliderGroup.setAttribute('y', `${subY}`);
sliderGroup.setAttribute('width', '150');
sliderGroup.setAttribute('height', '40');
sliderGroup.innerHTML = `<div style="width:100%;height:100%;background:#333;color:#fff;font-family:Heebo;padding:5px;">Zoom slider placeholder</div>`;
submenuGroup.appendChild(sliderGroup);
}
// Hover retention zone
const hoverZone = document.createElementNS(SVG_NS, "rect");
hoverZone.setAttribute('class','hover-zone');
hoverZone.setAttribute('x', `${width}`);
hoverZone.setAttribute('y', `${currentY}`);
hoverZone.setAttribute('width', '150');
hoverZone.setAttribute('height', `${subY - currentY}`);
itemGroup.appendChild(hoverZone);
}
currentY += height;
});
// --- Trigger logic ---
const activation = settings.active.ui.inPlayer.activation;
const triggerZone = settings.active.ui.inPlayer.triggerZoneDimensions;
const distanceOptions = settings.active.ui.inPlayer.distanceOptions;
function updateMenuVisibility(x: number, y: number) {
let show = false;
const rect = playerEl.getBoundingClientRect();
if(activation === 'player') {
if(x >= 0 && x <= rect.width && y >= 0 && y <= rect.height) show = true;
} else if(activation === 'trigger-zone' && triggerZone) {
const tx = rect.width * (triggerZone.offsetX / 100);
const ty = rect.height * (triggerZone.offsetY / 100);
if(x >= tx && x <= tx + triggerZone.width && y >= ty && y <= ty + triggerZone.height) show = true;
} else if(activation === 'distance' && distanceOptions) {
const dist = distanceOptions.distance / 100 * (distanceOptions.relativeTo === 'width' ? rect.width : rect.height);
const cx = rect.width/2;
const cy = rect.height/2;
if(Math.sqrt((x-cx)**2 + (y-cy)**2) <= dist) show = true;
}
menuGroup.style.display = show ? 'block' : 'none';
}
svg.addEventListener('mousemove', e => {
const rect = svg.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
updateMenuVisibility(x,y);
});
menuGroup.style.display = 'none';
return { svg, menuGroup };
}

View File

@ -5,7 +5,6 @@ const WebpackShellPluginNext = require('webpack-shell-plugin-next');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader'); const { VueLoaderPlugin } = require('vue-loader');
const path = require('path'); const path = require('path');
const aliases = require('./paths.config');
const config = { const config = {
watchOptions: { watchOptions: {
@ -33,7 +32,13 @@ const config = {
devtool: "source-map", devtool: "source-map",
resolve: { resolve: {
alias: aliases, // SEE: paths.conf.js alias: {
'@src': path.resolve(__dirname, 'src'),
'@csui': path.resolve(__dirname, 'src/csui'),
'@ui': path.resolve(__dirname, 'src/ui'),
'@components': path.resolve(__dirname, 'src/ui/components'),
'@': path.resolve(__dirname, 'src'),
},
// maybe we'll move vue stuff to TS some day, but today is not the day // maybe we'll move vue stuff to TS some day, but today is not the day
extensions: [ extensions: [
'.ts', '.tsx', '.ts', '.tsx',