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

14
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "ultrawidify",
"version": "6.3.994",
"version": "6.3.993",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ultrawidify",
"version": "6.3.994",
"version": "6.3.993",
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@mdi/font": "^7.4.47",
@ -16,7 +16,6 @@
"fs-extra": "^11.3.2",
"gl-matrix": "^3.4.4",
"json-cyclic": "1.0.2",
"json-difference": "^1.16.1",
"lodash": "^4.17.21",
"mdi-vue": "^3.0.13",
"typescript": "^5.9.3",
@ -13497,15 +13496,6 @@
"integrity": "sha512-K0Y9wQnrc13Ef+P8KY8SjFyVAz941CTrTVmu3oyQPExhaFFYXXLXnEuRi1tqYOS1B8WtFIxIwA7nHRJ64Uvf5Q==",
"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": {
"version": "1.0.2",
"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",
"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.",
"author": "Tamius Han <tamius.han@gmail.com>",
"scripts": {
@ -33,7 +33,6 @@
"fs-extra": "^11.3.2",
"gl-matrix": "^3.4.4",
"json-cyclic": "1.0.2",
"json-difference": "^1.16.1",
"lodash": "^4.17.21",
"mdi-vue": "^3.0.13",
"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 = {
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('postcss-nested'),
require('autoprefixer'),

View File

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

View File

@ -1,4 +1,4 @@
enum LegacyExtensionMode {
enum ExtensionMode {
AutoDisabled = -2,
Disabled = -1,
Default = 0,
@ -7,4 +7,4 @@ enum LegacyExtensionMode {
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 EmbeddedContentSettingsOverridePolicy from '../enums/EmbeddedContentSettingsOverridePolicy.enum'
import ExtensionMode from '../enums/ExtensionMode.enum'
import { SiteSupportLevel } from '../enums/SiteSupportLevel.enum'
import StretchType from '../enums/StretchType.enum'
import VideoAlignmentType from '../enums/VideoAlignmentType.enum'
export enum ExtensionEnvironment {
Normal = ExtensionMode.All,
Theater = ExtensionMode.Theater,
Fullscreen = ExtensionMode.FullScreen,
Normal = 'normal',
Theater = 'theater',
Fullscreen = 'fullscreen',
}
export interface DevUiConfig {
@ -49,6 +48,12 @@ interface RestrictionsSettings {
onlyAllowAutodetectionInFullScreen?: boolean; // if enabled, autodetection will only start once in full screen
}
interface ExtensionEnvironmentSettingsInterface {
normal: ExtensionMode,
theater: ExtensionMode,
fullscreen: ExtensionMode,
}
export interface CommandInterface {
action: string,
label: string,
@ -320,6 +325,8 @@ interface SettingsInterface {
ui: {
inPlayer: {
enabled: boolean, // Deprecated — moved to site settings
enabledFullscreenOnly: boolean, // Deprecated — moved to site settings
popupAlignment: 'left' | 'right',
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
@ -438,18 +445,18 @@ interface SettingsInterface {
}
export interface SiteSettingsInterface {
enable: ExtensionMode;
enableAard: ExtensionMode;
enableKeyboard: ExtensionMode;
enableUI: ExtensionMode;
enable: ExtensionEnvironmentSettingsInterface;
enableAard: ExtensionEnvironmentSettingsInterface;
enableKeyboard: ExtensionEnvironmentSettingsInterface;
enableUI: ExtensionEnvironmentSettingsInterface; // Lies! enableUI doesn't use 'theater' property (but uses the other two)
applyToEmbeddedContent: EmbeddedContentSettingsOverridePolicy; // presumed to be 'Always' if not defined
overrideWhenEmbedded?: boolean;
autocreated?: boolean;
type?: SiteSupportLevel;
defaultType: SiteSupportLevel;
type?: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
defaultType: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
// must be defined in @global and @empty
persistCSA?: CropModePersistence, // CSA - crop, stretch, alignment

View File

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

View File

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

View File

@ -110,7 +110,7 @@ export default class UWContent {
this.pageInfo = new PageInfo(this.eventBus, this.siteSettings, this.settings, this.logAggregator);
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) {
this.keyboardHandler.destroy();

View File

@ -1,44 +1,41 @@
// How to use:
// version: {ExtensionConf object, but only properties that get overwritten}
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 BrowserDetect from './BrowserDetect';
import SettingsInterface, { SiteSettingsInterface } from '../../common/interfaces/SettingsInterface';
import SettingsInterface from '../../common/interfaces/SettingsInterface';
import { _cp } from '../../common/js/utils';
import CropModePersistence from '../../common/enums/CropModePersistence.enum';
import AspectRatioType from '../../common/enums/AspectRatioType.enum';
import { update } from 'lodash';
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([
{
forVersion: '6.2.4',
updateFn: (userOptions: any, defaultOptions, logger?) => {
updateFn: (userOptions: SettingsInterface, defaultOptions) => {
for (const site in userOptions.sites) {
(userOptions as any).sites[site].enableUI = {
fullscreen: LegacyExtensionMode.Default,
theater: LegacyExtensionMode.Default,
normal: LegacyExtensionMode.Default,
userOptions.sites[site].enableUI = {
fullscreen: ExtensionMode.Default,
theater: ExtensionMode.Default,
normal: ExtensionMode.Default,
}
}
userOptions.sites['@global'].enableUI = {
fullscreen: userOptions.ui.inPlayer.enabled ? LegacyExtensionMode.Enabled : LegacyExtensionMode.Disabled,
theater: LegacyExtensionMode.Enabled,
normal: (userOptions.ui.inPlayer.enabled && !userOptions.ui.inPlayer.enabledFullscreenOnly) ? LegacyExtensionMode.Enabled : LegacyExtensionMode.Disabled
fullscreen: userOptions.ui.inPlayer.enabled ? ExtensionMode.Enabled : ExtensionMode.Disabled,
theater: ExtensionMode.Enabled,
normal: (userOptions.ui.inPlayer.enabled && !userOptions.ui.inPlayer.enabledFullscreenOnly) ? ExtensionMode.Enabled : ExtensionMode.Disabled
}
userOptions.sites['@empty'].enableUI = {
fullscreen: LegacyExtensionMode.Default,
theater: LegacyExtensionMode.Default,
normal: LegacyExtensionMode.Default,
fullscreen: ExtensionMode.Default,
theater: ExtensionMode.Default,
normal: ExtensionMode.Default,
}
}
}, {
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');
if (!userOptions.commands) {
@ -244,7 +241,7 @@ const ExtensionConfPatch = Object.freeze([
}
}, {
forVersion: '6.3.92',
updateFn: (userOptions: any) => {
updateFn: (userOptions: SettingsInterface) => {
// applyToEmbeddedContent is now an enum, and also no longer optional
for (const site in userOptions.sites) {
if (userOptions.sites[site].applyToEmbeddedContent === undefined) {
@ -273,41 +270,11 @@ const ExtensionConfPatch = Object.freeze([
},
{
forVersion: '6.3.98',
updateFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
upgradeFn: (userOptions: SettingsInterface, defaultOptions: SettingsInterface) => {
userOptions.aard = defaultOptions.aard;
userOptions.aardLegacy = defaultOptions.aardLegacy;
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 { AardPollingOptions } from '../lib/aard/enums/aard-polling-options.enum';
import { AardSubtitleCropMode } from '../lib/aard/enums/aard-subtitle-crop-mode.enum';
import { SiteSupportLevel } from '../../common/enums/SiteSupportLevel.enum';
if(Debug.debug)
console.log("Loading: ExtensionConf.js");
@ -263,6 +262,8 @@ const ExtensionConf: SettingsInterface = {
ui: {
inPlayer: {
enabled: true, // enable by default on new installs
enabledFullscreenOnly: false,
minEnabledWidth: 0.75,
minEnabledHeight: 0.75,
activation: 'player',
@ -756,6 +757,65 @@ const ExtensionConf: SettingsInterface = {
arguments: {
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: {
@ -802,14 +862,29 @@ const ExtensionConf: SettingsInterface = {
// to avoid writing this multiple times. Tags:
// #g — only available in @global
// #s — only available for specific site
enable: ExtensionMode.Disabled,
enableAard: ExtensionMode.Disabled,
enableKeyboard: ExtensionMode.Disabled,
enableUI: ExtensionMode.FullScreen,
enable: { // How should extension work:
fullscreen: ExtensionMode.Disabled, // 'enabled' - work everywhere except blacklist
theater: ExtensionMode.Disabled, //
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,
defaultType: SiteSupportLevel.Unknown,
defaultType: 'unknown',
persistCSA: CropModePersistence.Disabled,
defaults: {
@ -819,15 +894,30 @@ const ExtensionConf: SettingsInterface = {
}
},
"@empty": { // placeholder settings object with fallbacks to @global
enable: ExtensionMode.Default,
enableAard: ExtensionMode.Default,
enableKeyboard: ExtensionMode.Default,
enableUI: ExtensionMode.Default,
enable: {
fullscreen: ExtensionMode.Default,
theater: 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,
type: SiteSupportLevel.UserDefined,
defaultType: SiteSupportLevel.UserDefined,
type: 'user-defined',
defaultType: 'user-defined',
persistCSA: CropModePersistence.Default,
defaults: {
crop: null,
@ -836,22 +926,37 @@ const ExtensionConf: SettingsInterface = {
}
},
"www.youtube.com" : {
enable: ExtensionMode.All,
enableAard: ExtensionMode.All,
enableKeyboard: ExtensionMode.All,
enableUI: ExtensionMode.All,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
overrideWhenEmbedded: true,
override: false, // ignore value localStorage in favour of this
type: SiteSupportLevel.OfficialSupport, // is officially supported? (Alternatives are 'community' and 'user-defined')
defaultType: SiteSupportLevel.OfficialSupport, // if user mucks around with settings, type changes to 'user-defined'.
type: 'official', // is officially supported? (Alternatives are 'community' and '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
activeDOMConfig: 'official',
DOMConfig: {
'official': {
type: SiteSupportLevel.OfficialSupport,
type: 'official',
elements: {
player: {
manual: true,
@ -862,23 +967,38 @@ const ExtensionConf: SettingsInterface = {
}
},
"www.youtube-nocookie.com": {
enable: ExtensionMode.All,
enableAard: ExtensionMode.All,
enableKeyboard: ExtensionMode.All,
enableUI: ExtensionMode.FullScreen,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
overrideWhenEmbedded: true,
override: false, // ignore value localStorage in favour of this
type: SiteSupportLevel.OfficialSupport, // is officially supported? (Alternatives are 'community' and 'user-defined')
defaultType: SiteSupportLevel.OfficialSupport, // if user mucks around with settings, type changes to 'user-defined'.
type: 'official', // is officially supported? (Alternatives are 'community' and '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
activeDOMConfig: 'official',
DOMConfig: {
'official': {
type: SiteSupportLevel.OfficialSupport,
type: 'official',
elements: {
player: {
manual: true,
@ -889,28 +1009,59 @@ const ExtensionConf: SettingsInterface = {
}
},
"www.netflix.com" : {
enable: ExtensionMode.Theater,
enableAard: ExtensionMode.Disabled,
enableKeyboard: ExtensionMode.All,
enableUI: ExtensionMode.All,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
override: false,
type: SiteSupportLevel.CommunitySupport,
defaultType: SiteSupportLevel.CommunitySupport,
type: 'community',
defaultType: 'community',
},
"www.disneyplus.com" : {
enable: ExtensionMode.Theater,
enableAard: ExtensionMode.Theater,
enableKeyboard: ExtensionMode.All,
enableUI: ExtensionMode.FullScreen,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
type: SiteSupportLevel.CommunitySupport,
defaultType: SiteSupportLevel.CommunitySupport,
type: 'community',
defaultType: 'community',
activeDOMConfig: 'community-mstefan99',
DOMConfig: {
'community-mstefan99': {
type: SiteSupportLevel.OfficialSupport,
type: 'official',
elements: {
player: {
manual: true,
@ -926,28 +1077,58 @@ const ExtensionConf: SettingsInterface = {
}
},
"www.twitch.tv": {
enable: ExtensionMode.All,
enableAard: ExtensionMode.All,
enableKeyboard: ExtensionMode.All,
enableUI: ExtensionMode.FullScreen,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
type: SiteSupportLevel.OfficialSupport,
defaultType: SiteSupportLevel.OfficialSupport,
type: 'official',
defaultType: 'official',
},
"old.reddit.com" : {
enable: ExtensionMode.Disabled,
enableAard: ExtensionMode.Disabled,
enableKeyboard: ExtensionMode.Disabled,
enableUI: ExtensionMode.Disabled,
enable: {
fullscreen: ExtensionMode.Disabled,
theater: 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,
type: SiteSupportLevel.OfficialBlacklist,
defaultType: SiteSupportLevel.OfficialBlacklist,
type: 'officially-disabled',
defaultType: 'officially-disabled',
activeDOMConfig: 'official',
DOMConfig: {
'official': {
type: SiteSupportLevel.OfficialSupport,
type: 'official',
// customCss: 'video {\n width: 100% !important;\n height: 100% !important;\n}',
elements: {
player: {
@ -959,18 +1140,33 @@ const ExtensionConf: SettingsInterface = {
},
},
"www.reddit.com" : {
enable: ExtensionMode.Disabled,
enableAard: ExtensionMode.Disabled,
enableKeyboard: ExtensionMode.Disabled,
enableUI: ExtensionMode.Disabled,
enable: {
fullscreen: ExtensionMode.Disabled,
theater: 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,
type: SiteSupportLevel.OfficialBlacklist,
defaultType: SiteSupportLevel.OfficialBlacklist,
type: 'officially-disabled',
defaultType: 'officially-disabled',
activeDOMConfig: 'official',
DOMConfig: {
'official': {
type: SiteSupportLevel.OfficialSupport,
type: 'official',
// customCss: 'video {\n width: 100% !important;\n height: 100% !important;\n}',
elements: {
player: {
@ -982,28 +1178,58 @@ const ExtensionConf: SettingsInterface = {
},
},
"imgur.com": {
enable: ExtensionMode.Disabled,
enableAard: ExtensionMode.Disabled,
enableKeyboard: ExtensionMode.Disabled,
enableUI: ExtensionMode.Disabled,
enable: {
fullscreen: ExtensionMode.Disabled,
theater: 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,
type: SiteSupportLevel.OfficialBlacklist,
defaultType: SiteSupportLevel.OfficialBlacklist,
type: 'officially-disabled',
defaultType: 'officially-disabled',
},
"www.wakanim.tv": {
enable: ExtensionMode.All,
enableAard: ExtensionMode.All,
enableKeyboard: ExtensionMode.All,
enableUI: ExtensionMode.FullScreen,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
type: SiteSupportLevel.CommunitySupport,
defaultType: SiteSupportLevel.CommunitySupport,
type: 'community',
defaultType: 'community',
activeDOMConfig: 'community',
DOMConfig: {
'community': {
type: SiteSupportLevel.CommunitySupport,
type: 'community',
elements: {
player: {
manual: true,
@ -1014,34 +1240,65 @@ const ExtensionConf: SettingsInterface = {
},
},
"app.plex.tv": {
enable: ExtensionMode.Theater,
enableAard: ExtensionMode.Theater,
enableKeyboard: ExtensionMode.Theater,
enableUI: ExtensionMode.FullScreen,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
type: SiteSupportLevel.CommunitySupport,
defaultType: SiteSupportLevel.CommunitySupport,
type: 'community',
defaultType: 'community',
activeDOMConfig: 'community',
DOMConfig: {
'community': {
type: SiteSupportLevel.CommunitySupport,
type: 'community',
// customCss: "body {\n background-color: #000;\n}\n\n.application {\n background-color: #000;\n}"
}
}
},
"metaivi.com": {
enable: ExtensionMode.Theater,
enableAard: ExtensionMode.Theater,
enableKeyboard: ExtensionMode.Theater,
enableUI: ExtensionMode.FullScreen,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
type: SiteSupportLevel.CommunitySupport,
defaultType: SiteSupportLevel.CommunitySupport,
type: "community",
defaultType: "community",
activeDOMConfig: 'community',
DOMConfig: {
'community': {
type: SiteSupportLevel.CommunitySupport,
type: 'community',
elements: {
video: {
nodeCss: {'position': 'absolute !important'}
@ -1051,18 +1308,33 @@ const ExtensionConf: SettingsInterface = {
},
},
"piped.kavin.rocks": {
enable: ExtensionMode.Theater,
enableAard: ExtensionMode.Theater,
enableKeyboard: ExtensionMode.Theater,
enableUI: ExtensionMode.FullScreen,
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
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,
type: SiteSupportLevel.CommunitySupport,
defaultType: SiteSupportLevel.CommunitySupport,
type: "community",
defaultType: "community",
activeDOMConfig: 'community',
DOMConfig: {
'community': {
type: SiteSupportLevel.CommunitySupport,
type: 'community',
// 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);
return;
}
if (this.siteSettings.data.enableAard <= this.videoData.player.environment) {
if (this.siteSettings.data.enableAard[this.videoData.player.environment] === ExtensionMode.Enabled) {
this.start();
} else {
this.stop();

View File

@ -1,4 +1,5 @@
import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
import ExtensionMode from '@src/common/enums/ExtensionMode.enum';
import { ArVariant } from '@src/common/interfaces/ArInterface';
import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
import EventBus from '../EventBus';
@ -15,10 +16,15 @@ import { GlDebugCanvas, GlDebugType } from './gl/GlDebugCanvas';
import { AardCanvasStore } from './interfaces/aard-canvas-store.interface';
import { AardDetectionSample, generateSampleArray, resetSamples } from './interfaces/aard-detection-sample.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 { ComponentLogger } from '../logging/ComponentLogger';
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 { result } from 'lodash';
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);
return;
}
if (this.videoData.player.environment <= this.siteSettings.data.enableAard) {
if (this.siteSettings.data.enableAard[this.videoData.player.environment] === ExtensionMode.Enabled) {
this.start();
} else {
this.stop();

View File

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

View File

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

View File

@ -6,10 +6,9 @@ import ExtensionConfPatch from '../../conf/ExtConfPatches';
import SettingsInterface from '../../../common/interfaces/SettingsInterface';
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import { GetSiteSettingsOptions, SiteSettings } from './SiteSettings';
import { SettingsSnapshot, SettingsSnapshotManager } from './SettingsSnapshotManager';
import { SettingsSnapshotManager } from './SettingsSnapshotManager';
import { ComponentLogger } from '../logging/ComponentLogger';
import { LogAggregator } from '../logging/LogAggregator';
import { getDiff } from 'json-difference'
if(process.env.CHANNEL !== 'stable'){
console.info("Loading Settings");
@ -96,49 +95,28 @@ class Settings {
if (!changes.uwSettings) {
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);
// if (changes['uwSettings'] && 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 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.logger?.info('storageOnChange', 'Does parsedSettings.preventReload exist?', parsedSettings.preventReload, "Does callback exist?", !!this.onSettingsChanged);
if (!parsedSettings.preventReload) {
this.active.preventReload = true;
try {
for (const fn of this.onChangedCallbacks) {
try {
fn();
} 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) {
this.onSettingsChanged();
}
this.logger?.info('storageOnChange', 'Update callback finished.')
} catch (e) {
this.logger?.error('storageOnChange', "CALLING UPDATE CALLBACK FAILED. Reason:", e)
@ -148,7 +126,7 @@ class Settings {
try {
fn();
} 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) {
@ -234,7 +212,7 @@ class Settings {
private findFirstNecessaryPatch(version) {
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);
if (index === -1) {
@ -243,30 +221,26 @@ class Settings {
}
// save current settings object
if (!options?.skipSnapshot) {
this.snapshotManager.createSnapshot(
JSON.parse(JSON.stringify(currentSettings)),
{
label: 'Pre-migration snapshot',
isAutomatic: true
}
);
}
const currentSettings = this.active;
this.snapshotManager.createSnapshot(
JSON.parse(JSON.stringify(currentSettings)),
{
label: 'Pre-migration snapshot',
isAutomatic: true
}
);
// this.active.newFeatureTracker = {};
// apply all remaining patches
const pendingPatchesCount = ExtensionConfPatch.length - index;
this.logger?.info('applySettingsPatches', `There are ${pendingPatchesCount} settings patches to apply`);
const defaultSettings = this.getDefaultSettings();
this.logger?.info('applySettingsPatches', `There are ${ExtensionConfPatch.length - index} settings patches to apply`);
while (index < ExtensionConfPatch.length) {
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) {
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(currentSettings, defaultSettings, this.logger);
this.logger.log('applySettingsPatches', `Patch for version ${ExtensionConfPatch[index].forVersion} applied. (${pendingPatchesCount - (ExtensionConfPatch.length - index)} / ${pendingPatchesCount})`);
updateFn(this.active, this.getDefaultSettings());
} catch (e) {
this.logger?.error('applySettingsPatches', 'Failed to execute update function. Keeping settings object as-is. Error:', e);
}
@ -274,37 +248,30 @@ class Settings {
index++;
}
return currentSettings;
}
async init(options?: {dryRun?: boolean, snapshot?: SettingsSnapshot, skipSnapshot?: boolean}) {
let settings;
async init() {
let settings = await this.get();
if (options?.snapshot) {
this.logger?.info('init', 'Snapshot was provided — will load settings from snapshot.', options.snapshot);
settings = options.snapshot.settings;
} else if (settings?.dev?.loadFromSnapshot) {
if (settings?.dev?.loadFromSnapshot) {
this.logger?.info('init', 'Dev mode is enabled, Loading settings from snapshot:', settings.dev.loadFromSnapshot);
const snapshot = await this.snapshotManager.getSnapshot();
if (snapshot) {
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
// | since new installs ship with updates by default, no patching is
// | 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) {
this.logger?.info('init', "Configuration fetched from storage:", settings,
"\nlast saved with:", settings.version,
"\ncurrent version:", currentVersion
"\ncurrent version:", this.version
);
}
@ -317,57 +284,40 @@ class Settings {
'\nsettings:',
settings
);
this.active = this.getDefaultSettings();
this.active.version = this.version;
await this.save();
settings = this.getDefaultSettings();
settings.version = currentVersion;
if (!options?.dryRun) {
this.active = settings;
this.version = currentVersion;
await this.save();
}
return settings;
return this.active;
}
// if there's settings, set saved object as active settings
this.active = settings;
// if version number is undefined, we make it defined
// this should only happen on first extension initialization
if (!settings.version) {
settings.version = currentVersion;
if (!options?.dryRun) {
this.active = settings;
this.version = currentVersion;
await this.save();
}
return settings;
if (!this.active.version) {
this.active.version = this.version;
await this.save();
return this.active;
}
// 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.");
if (!options?.dryRun) {
this.active = settings;
}
return settings;
return this.active;
}
// 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
settings.whatsNewChecked = false;
this.active.whatsNewChecked = false;
// update settings version to current
settings.version = currentVersion;
this.active.version = this.version;
if (!options?.dryRun) {
this.active = settings;
this.version = currentVersion;
await this.save();
}
return settings;
await this.save();
return this.active;
}
async get(): Promise<SettingsInterface | undefined> {
@ -487,11 +437,6 @@ class Settings {
removeAfterChangeListener(fn: () => void): void {
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;

View File

@ -1,12 +1,11 @@
import CropModePersistence from '../../../common/enums/CropModePersistence.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 Settings from './Settings';
import StretchType from '../../../common/enums/StretchType.enum';
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
import EmbeddedContentSettingsOverridePolicy from '../../../common/enums/EmbeddedContentSettingsOverridePolicy.enum';
import { SiteSupportLevel } from '../../../common/enums/SiteSupportLevel.enum';
export interface GetSiteSettingsOptions {
@ -183,8 +182,6 @@ export class SiteSettings {
*/
private compileSettingsObject() {
const {siteSettings, usesSettingsFor} = this.getSettingsForSite(this.options);
console.log('got settings for site:', siteSettings, usesSettingsFor)
this.data = _cp(siteSettings);
this.usesSettingsFor = usesSettingsFor;
@ -216,10 +213,13 @@ export class SiteSettings {
}
for (const enableSegment of ['enable', 'enableAard', 'enableKeyboard', 'enableUI']) {
if (this.data[enableSegment] === undefined) {
if (!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) {
return ExtensionEnvironment.Fullscreen;
return 'fullscreen';
}
if (isTheater) {
return ExtensionEnvironment.Theater;
return 'theater';
}
return ExtensionEnvironment.Normal;
return 'normal';
}
/**
@ -355,9 +355,9 @@ export class SiteSettings {
* @param isFullscreen
* @returns ExtensionMode
*/
isEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): boolean {
isEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): ExtensionMode {
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
* @returns
*/
isAardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): boolean {
isAardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): ExtensionMode {
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
* @returns
*/
isKeyboardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): boolean {
isKeyboardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean): ExtensionMode {
const env = this._getEnvironment(isTheater, isFullscreen);
return env <= this.data.enableKeyboard;
return this.data.enableKeyboard[env];
}
//#endregion
@ -461,11 +461,11 @@ export class SiteSettings {
if (options.scripted && !this.settings.active.sites[targetSite]) {
this.settings.active.sites[targetSite] = _cp(this.settings.active.sites['@global']);
this.settings.active.sites[targetSite].autocreated = true;
this.settings.active.sites[targetSite].type = SiteSupportLevel.Unknown;
this.settings.active.sites[targetSite].type = 'unknown';
} else {
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].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
this.csuiScheme = this.getCsuiScheme();
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(/\/$/, "");
// UI will be initialized when setUiVisibility is called
@ -53,13 +53,17 @@ class UI {
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() {
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;
}
// 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();
@ -119,7 +123,6 @@ class UI {
}
loadIframe() {
return;
// in onMouseMove, we currently can't access this because we didn't
// do things the most properly
const uiURI = this.uiURI;
@ -339,7 +342,7 @@ class UI {
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;
}
@ -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) {
// if (routingData) {

View File

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

View File

@ -269,14 +269,14 @@ class PlayerData {
* @returns
*/
deferredUiInitialization(playerDimensions = this.dimensions) {
if (this.ui || this.siteSettings.data.enableUI === ExtensionMode.Disabled) {
if (this.ui || this.siteSettings.data.enableUI.fullscreen === ExtensionMode.Disabled) {
return;
}
if (
this.isFullscreen
|| (
this.siteSettings.data.enableUI >= ExtensionMode.FullScreen
this.siteSettings.data.enableUI.theater !== ExtensionMode.Disabled
&& playerDimensions.width > 1208
&& playerDimensions.height > 720
)
@ -478,7 +478,7 @@ class PlayerData {
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) {
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.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);
} else {
this.restoreAr();
@ -417,7 +417,7 @@ class VideoData {
}
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;
}

View File

@ -1,11 +1,27 @@
@import "tailwindcss";
@import "tailwindcss/utilities";
@import '@ui/res/styles/theme.css';
@import '@ui/res/styles/forms.css';
@import '@ui/res/styles/fonts.css';
@import '@ui/res/styles/buttons.css';
@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;
}
@import './ui/res/styles/forms.css';
@import './ui/res/styles/fonts.css';
@import './ui/res/styles/buttons.css';
html, body {
@apply bg-stone-950 text-stone-300;

View File

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

View File

@ -232,8 +232,8 @@
</template>
<script lang="ts">
import BrowserDetect from '@src/ext/conf/BrowserDetect';
import JsonEditor from '@components/common/JsonEditor.vue';
import BrowserDetect from '../../../ext/conf/BrowserDetect';
import JsonEditor from '@csui/src/components/JsonEditor';
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 { 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>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>
<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>
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.
</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>
</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>
Keyboard shortcut settings have been split from UI settings, and are now presented in list form on the settings page.
</li>
@ -31,12 +23,6 @@
</li>
</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>
<script>
import BrowserDetect from '@src/ext/conf/BrowserDetect';

View File

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

View File

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

View File

@ -30,7 +30,7 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import KeyboardShortcutParserMixin from '@ui/utils/mixins/KeyboardShortcutParserMixin.vue';
import KeyboardShortcutParserMixin from '../../../../csui/src/utils/KeyboardShortcutParserMixin';
import EditShortcutButton from './EditShortcutButton.vue';
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 NewAspectRatioForm from './Components/NewAspectRatioForm.vue';
import BrowserDetect from '@src/ext/conf/BrowserDetect';
import CropOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/CropOptionsPanel.vue'
import StretchOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/StretchOptionsPanel.vue'
import ZoomOptionsPanel from '@csui/src/PlayerUiPanels/PanelComponents/VideoSettings/ZoomOptionsPanel.vue'

View File

@ -65,7 +65,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
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({
@ -95,7 +95,7 @@ export default defineComponent({
})
</script>
<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 {
width: 16rem;

View File

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

View File

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

View File

@ -6,10 +6,7 @@
">
<!-- page content -->
<div
class="w-full h-[100dvh] overflow-hidden flex flex-col"
:class="{'max-w-[1920px]': !isDebugging}"
>
<div class="w-full max-w-[1920px] h-[100dvh] overflow-hidden flex flex-col">
<PopupHead
v-if="role === 'popup' && settings && siteSettings && eventBus"
:settings="settings"
@ -35,7 +32,6 @@
:logger="logger"
:inPlayer="false"
:site="site"
@debugStatusChanged="setDebugStatus"
>
</SettingsWindowContent>
</div>
@ -55,7 +51,7 @@ import EventBus from '@src/ext/lib/EventBus';
import CommsClient, { CommsOrigin } from '@src/ext/lib/comms/CommsClient';
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({
components: {
@ -74,7 +70,6 @@ export default defineComponent({
settingsInitialized: false,
role: 'settings',
initialPath: undefined as string[] | undefined,
isDebugging: false,
}
},
async created() {
@ -92,18 +87,10 @@ export default defineComponent({
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();
this.settingsInitialized = true;
},
methods: {
setDebugStatus(isDebugging: boolean) {
this.isDebugging = isDebugging;
},
updateConfig() {
this.settings.init();
this.$nextTick( () => this.$forceUpdate());

View File

@ -1,6 +1,6 @@
@font-face {
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-stretch: 75% 125%;
font-style: normal;
@ -9,7 +9,7 @@
@font-face {
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-style: normal;
font-display: swap;
@ -17,7 +17,7 @@
@font-face {
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-style: italic;
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 { VueLoaderPlugin } = require('vue-loader');
const path = require('path');
const aliases = require('./paths.config');
const config = {
watchOptions: {
@ -33,7 +32,13 @@ const config = {
devtool: "source-map",
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
extensions: [
'.ts', '.tsx',