diff --git a/package.json b/package.json index d457a550..3ba4be94 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.45.1", + "version": "4.46.0", "description": "FrankerFaceZ is a Twitch enhancement suite.", "private": true, "license": "Apache-2.0", diff --git a/src/modules/chat/tokenizers.jsx b/src/modules/chat/tokenizers.jsx index 30b8fccd..2e627df1 100644 --- a/src/modules/chat/tokenizers.jsx +++ b/src/modules/chat/tokenizers.jsx @@ -22,7 +22,8 @@ const SHRINK_X = MODIFIER_FLAGS.ShrinkX, const EMOTE_CLASS = 'chat-image chat-line__message--emote', //WHITESPACE = /^\s*$/, //LINK_REGEX = /([^\w@#%\-+=:~])?((?:(https?:\/\/)?(?:[\w@#%\-+=:~]+\.)+[a-z]{2,6}(?:\/[\w./@#%&()\-+=:?~]*)?))([^\w./@#%&()\-+=:?~]|\s|$)/g, - NEW_LINK_REGEX = /(?:(https?:\/\/)?((?:[\w#%\-+=:~]+\.)+[a-z]{2,10}(?:\/[\w./#%&@()\-+=:?~]*)?))/g, + NEW_LINK_REGEX = /(?:(https?:\/\/)?((?:[\w#%\-+=:~]+\.)+[a-z]{2,10}(?:\/[\w./#%&@()\-+=:?~]*[^\.!,?])?))/g, + //OLD_NEW_LINK_REGEX = /(?:(https?:\/\/)?((?:[\w#%\-+=:~]+\.)+[a-z]{2,10}(?:\/[\w./#%&@()\-+=:?~]*)?))/g, //MENTION_REGEX = /([^\w@#%\-+=:~])?(@([^\u0000-\u007F]+|\w+)+)([^\w./@#%&()\-+=:?~]|\s|$)/g; // eslint-disable-line no-control-regex MENTION_REGEX = /^(['"*([{<\\/]*)(@)((?:[^\u0000-\u007F]|[\w-])+)(?:\b|$)/; // eslint-disable-line no-control-regex @@ -1275,9 +1276,9 @@ export const AddonEmotes = { if ( effects ) { this.emotes.ensureEffect(effects); - if ( (effects & SHRINK_X) === SHRINK_X ) + if ( (effects & SHRINK_X) === SHRINK_X && this.emotes.effects_enabled?.ShrinkX ) style.width *= 0.5; - if ( (effects & STRETCH_X) === STRETCH_X ) + if ( (effects & STRETCH_X) === STRETCH_X && this.emotes.effects_enabled?.GrowX ) style.width *= 2; /*if ( (effects & SHRINK_Y) === SHRINK_Y ) style.height *= 0.5; @@ -1538,11 +1539,11 @@ export const AddonEmotes = { let changed = false; - if ( (effects & SHRINK_X) === SHRINK_X ) { + if ( (effects & SHRINK_X) === SHRINK_X && this.emotes.effects_enabled?.ShrinkX ) { style.width *= 0.5; changed = true; } - if ( (effects & STRETCH_X) === STRETCH_X ) { + if ( (effects & STRETCH_X) === STRETCH_X && this.emotes.effects_enabled?.GrowX ) { style.width *= 2; changed = true; } diff --git a/src/modules/emote_card/components/card.vue b/src/modules/emote_card/components/card.vue index 23f393a3..f5bfcb9c 100644 --- a/src/modules/emote_card/components/card.vue +++ b/src/modules/emote_card/components/card.vue @@ -21,7 +21,7 @@ > -
+

{{ emote ? emote.name : raw_emote.name }}

@@ -76,7 +76,7 @@ v-if="canFavorite" :data-title="favoriteLabel" :aria-label="favoriteLabel" - class="viewer-card-drag-cancel tw-align-self-start tw-align-items-center tw-align-middle tw-border-radius-medium tw-button-icon tw-core-button tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative ffz-tooltip" + class="tw-flex-shrink-0 viewer-card-drag-cancel tw-align-self-start tw-align-items-center tw-align-middle tw-border-radius-medium tw-button-icon tw-core-button tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative ffz-tooltip" @click="toggleFavorite" > diff --git a/src/modules/main_menu/components/action-editor.vue b/src/modules/main_menu/components/action-editor.vue index 20f721f0..57ae5bf2 100644 --- a/src/modules/main_menu/components/action-editor.vue +++ b/src/modules/main_menu/components/action-editor.vue @@ -746,8 +746,10 @@ export default { if ( this.$refs.key_meta.checked ) i |= 8; - this.edit_data.display.hover = this.$refs.key_hover.checked; this.edit_data.display.keys = i; + + if ( this.has_hover_modifier ) + this.edit_data.display.hover = this.$refs.key_hover.checked; }, edit() { diff --git a/src/modules/main_menu/components/addon-list.vue b/src/modules/main_menu/components/addon-list.vue index d8fa0c5e..4a94f10a 100644 --- a/src/modules/main_menu/components/addon-list.vue +++ b/src/modules/main_menu/components/addon-list.vue @@ -63,6 +63,16 @@ />
+
+ {{ t('addon.displaying', 'Displaying {visible, number} of {total, plural, one {# add-on} other {# add-ons} }.', { + visible: visible_addons.length, + total: listed_addons.length + }) }} + +
+
this.shouldShow(addon)); }, + listed_addons() { + return this.sorted_addons.filter(addon => ! addon.unlisted) + }, + sorted_addons() { const addons = this.item.getAddons(); diff --git a/src/settings/components/monitor.vue b/src/settings/components/monitor.vue new file mode 100644 index 00000000..71622a82 --- /dev/null +++ b/src/settings/components/monitor.vue @@ -0,0 +1,65 @@ + + + \ No newline at end of file diff --git a/src/settings/filters.js b/src/settings/filters.js index d7688c7b..3571b931 100644 --- a/src/settings/filters.js +++ b/src/settings/filters.js @@ -37,6 +37,21 @@ export const Invert = { editor: () => import(/* webpackChunkName: 'main-menu' */ './components/nested.vue') }; +export const And = { + createTest(config, rule_types, rebuild) { + return createTester(config, rule_types, false, false, rebuild); + }, + + childRules: true, + + tall: true, + title: 'And', + i18n: 'settings.filter.and', + + default: () => [], + editor: () => import(/* webpackChunkName: 'main-menu' */ './components/nested.vue') +}; + export const Or = { createTest(config, rule_types, rebuild) { return createTester(config, rule_types, false, true, rebuild); @@ -83,7 +98,7 @@ export const Constant = { default: true, editor: () => import(/* webpackChunkName: 'main-menu' */ './components/basic-toggle.vue') -} +}; // Context Stuff @@ -374,4 +389,54 @@ export const Title = { }), editor: () => import(/* webpackChunkName: 'main-menu' */ './components/title.vue') -}; \ No newline at end of file +}; + +// Monitor Stuff + +export let Monitor = null; + +if ( window.getScreenDetails ) { + + Monitor = { + _used: false, + details: undefined, + + used: () => { + const out = Monitor._used; + Monitor._used = false; + return out; + }, + + createTest(config = {}, _, reload) { + if ( ! config.label ) + return () => false; + + Monitor._used = true; + if ( Monitor.details === undefined ) + FrankerFaceZ.get().resolve('settings').createMonitorUpdate().then(() => { + reload(); + }); + + return () => { + Monitor._used = true; + const details = Monitor.details, + screen = details?.currentScreen; + + if ( ! screen ) + return false; + + return screen.label === config.label; + }; + }, + + default: () => ({ + label: null + }), + + title: 'Current Monitor', + i18n: 'settings.filter.monitor', + + editor: () => import(/* webpackChunkName: 'main-menu' */ './components/monitor.vue') + }; + +} diff --git a/src/settings/index.js b/src/settings/index.js index 083ff1bd..b537da81 100644 --- a/src/settings/index.js +++ b/src/settings/index.js @@ -110,7 +110,7 @@ export default class SettingsManager extends Module { this.filters = {}; for(const key in FILTERS) - if ( has(FILTERS, key) ) + if ( has(FILTERS, key) && FILTERS[key] ) this.filters[key] = FILTERS[key]; @@ -247,8 +247,28 @@ export default class SettingsManager extends Module { } + async createMonitorUpdate() { + const Monitor = FILTERS?.Monitor; + if ( ! Monitor || Monitor.details !== undefined ) + return; + + Monitor.details = null; + try { + Monitor.details = await window.getScreenDetails(); + Monitor.details.addEventListener('currentscreenchange', () => { + for(const context of this.__contexts) + context.selectProfiles(); + }); + + } catch(err) { + this.log.error('Unable to get monitor details', err); + Monitor.details = false; + } + } + + updateClock() { - const captured = require('./filters').Time.captured(); + const captured = FILTERS?.Time?.captured?.(); if ( ! captured?.length ) return; @@ -918,7 +938,19 @@ export default class SettingsManager extends Module { _saveProfiles() { - this.provider.set('profiles', this.__profiles.filter(prof => ! prof.ephemeral).map(prof => prof.data)); + const out = this.__profiles.filter(prof => ! prof.ephemeral).map(prof => prof.data); + + // Ensure that we always have a non-ephemeral profile. + if ( ! out ) { + this.createProfile({ + name: 'Default Profile', + i18n_key: 'setting.profiles.default', + description: 'Settings that apply everywhere on Twitch.' + }); + return; + } + + this.provider.set('profiles', out); for(const context of this.__contexts) context.selectProfiles(); diff --git a/src/sites/twitch-twilight/modules/chat/emote_menu.jsx b/src/sites/twitch-twilight/modules/chat/emote_menu.jsx index b8008b11..c020e890 100644 --- a/src/sites/twitch-twilight/modules/chat/emote_menu.jsx +++ b/src/sites/twitch-twilight/modules/chat/emote_menu.jsx @@ -1212,7 +1212,7 @@ export default class EmoteMenu extends Module { requestAnimationFrame(() => { const el = this.nav_ref?.querySelector?.(`button[data-key="${this.state.active_nav}"]`); if ( el ) - el.scrollIntoView({block: 'nearest'}); + el.scrollIntoView({block: 'nearest', inline: 'start'}); }); } @@ -1342,7 +1342,7 @@ export default class EmoteMenu extends Module { const el = this.ref?.querySelector?.(`section[data-key="${key}"]`); if ( el ) { this.lock_active = true; - el.scrollIntoView(); + el.scrollIntoView({block: 'nearest', inline: 'start'}); this.setState({ active_nav: key }); @@ -1381,7 +1381,7 @@ export default class EmoteMenu extends Module { el = set && this.ref?.querySelector?.(`section[data-key="${set.key}"]`); if ( el ) - el.scrollIntoView(); + el.scrollIntoView({block: 'nearest', inline: 'start'}); return; } diff --git a/src/utilities/fonts.js b/src/utilities/fonts.js index 4d936ea0..aa606839 100644 --- a/src/utilities/fonts.js +++ b/src/utilities/fonts.js @@ -22,7 +22,9 @@ const KNOWN_FONTS = [ 'Comic Sans MS', ]; -export const VALID_FONTS = KNOWN_FONTS.filter(font => document.fonts.check(`16px ${font}`)).sort(); +export const VALID_FONTS = document.fonts?.check + ? KNOWN_FONTS.filter(font => document.fonts.check(`16px ${font}`)).sort() + : KNOWN_FONTS.sort(); /* Google Font Handling */