1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-27 21:05:53 +00:00
* Added: When searching in the FFZ Control Center, you can now use the tag `@modified` to filter by settings that have been changed in the current profile.
* Added: Add-ons now have Changelog buttons that navigate to a changelog showing only entries for that add-on.
* Changed: The Changelog pages now has nicer formatting for each commit, including add-on icons and clickable links to add-on sub-pages when viewing the Add-on Changelog.
* API Added: To prevent FrankerFaceZ from loading into a page, include `disable_frankerfacez` in the URL query parameters.
* Experiment Changed: Fix incorrect roll-out percentage for API-Based Link Lookups. This should be fully enabled.
* Experiment Changed: Lower the percentage of users in the MQTT-Based PubSub experiment.
This commit is contained in:
SirStendec 2023-11-06 20:47:19 -05:00
parent 5046088bf7
commit ba72969c51
17 changed files with 416 additions and 72 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "frankerfacez", "name": "frankerfacez",
"author": "Dan Salvato LLC", "author": "Dan Salvato LLC",
"version": "4.59.1", "version": "4.60.0",
"description": "FrankerFaceZ is a Twitch enhancement suite.", "description": "FrankerFaceZ is a Twitch enhancement suite.",
"private": true, "private": true,
"license": "Apache-2.0", "license": "Apache-2.0",

View file

@ -7,7 +7,7 @@
import Module from 'utilities/module'; import Module from 'utilities/module';
import { EXTENSION, SERVER_OR_EXT } from 'utilities/constants'; import { EXTENSION, SERVER_OR_EXT } from 'utilities/constants';
import { createElement } from 'utilities/dom'; import { createElement } from 'utilities/dom';
import { timeout, has } from 'utilities/object'; import { timeout, has, deep_copy } from 'utilities/object';
import { getBuster } from 'utilities/time'; import { getBuster } from 'utilities/time';
const fetchJSON = (url, options) => fetch(url, options).then(r => r.ok ? r.json() : null).catch(() => null); const fetchJSON = (url, options) => fetch(url, options).then(r => r.ok ? r.json() : null).catch(() => null);
@ -51,6 +51,7 @@ export default class AddonManager extends Module {
getExtraTerms: () => Object.values(this.addons).map(addon => addon.search_terms), getExtraTerms: () => Object.values(this.addons).map(addon => addon.search_terms),
getFFZ: () => this,
isReady: () => this.enabled, isReady: () => this.enabled,
getAddons: () => Object.values(this.addons), getAddons: () => Object.values(this.addons),
hasAddon: id => this.hasAddon(id), hasAddon: id => this.hasAddon(id),
@ -216,6 +217,16 @@ export default class AddonManager extends Module {
this.addons[id] = [addon.id]; this.addons[id] = [addon.id];
} }
if ( ! old )
this.settings.addUI(`addon-changelog.${addon.id}`, {
path: `Add-Ons > Changelog > ${addon.name}`,
component: 'changelog',
force_seen: true,
addons: true,
addon: deep_copy(addon),
getFFZ: () => this
});
this.emit(':added'); this.emit(':added');
} }

View file

@ -5,6 +5,9 @@
if ( /^(?:localhost\.rig|blog|im|chatdepot|tmi|api|brand|dev|gql|passport)\./.test(location.hostname) ) if ( /^(?:localhost\.rig|blog|im|chatdepot|tmi|api|brand|dev|gql|passport)\./.test(location.hostname) )
return; return;
if ( /disable_frankerfacez/.test(location.search) )
return;
const DEBUG = localStorage.ffzDebugMode == 'true' && document.body.classList.contains('ffz-dev'), const DEBUG = localStorage.ffzDebugMode == 'true' && document.body.classList.contains('ffz-dev'),
HOST = location.hostname, HOST = location.hostname,
SERVER = DEBUG ? '//localhost:8000' : '//cdn.frankerfacez.com', SERVER = DEBUG ? '//localhost:8000' : '//cdn.frankerfacez.com',

View file

@ -5,6 +5,9 @@
if ( /^(?:localhost\.rig|blog|im|chatdepot|tmi|api|brand|dev|gql|passport)\./.test(location.hostname) ) if ( /^(?:localhost\.rig|blog|im|chatdepot|tmi|api|brand|dev|gql|passport)\./.test(location.hostname) )
return; return;
if ( /disable_frankerfacez/.test(location.search) )
return;
const browser = globalThis.browser ?? globalThis.chrome, const browser = globalThis.browser ?? globalThis.chrome,
HOST = location.hostname, HOST = location.hostname,

View file

@ -11,17 +11,17 @@
"name": "API-Based Link Lookups", "name": "API-Based Link Lookups",
"description": "Use the new API to look up links instead of the socket cluster.", "description": "Use the new API to look up links instead of the socket cluster.",
"groups": [ "groups": [
{"value": true, "weight": 30}, {"value": true, "weight": 0},
{"value": "cf", "weight": 10}, {"value": "cf", "weight": 100},
{"value": false, "weight": 60} {"value": false, "weight": 0}
] ]
}, },
"cf_pubsub": { "cf_pubsub": {
"name": "MQTT-Based PubSub", "name": "MQTT-Based PubSub",
"description": "An experimental new pubsub system that should be more reliable than the existing socket cluster.", "description": "An experimental new pubsub system that should be more reliable than the existing socket cluster.",
"groups": [ "groups": [
{"value": true, "weight": 10}, {"value": true, "weight": 5},
{"value": false, "weight": 90} {"value": false, "weight": 95}
] ]
} }
} }

View file

@ -60,6 +60,7 @@
:item="item" :item="item"
:context="context" :context="context"
@navigate="navigate" @navigate="navigate"
@change-item="changeItem"
/> />
</div> </div>
@ -68,7 +69,7 @@
visible: visible_addons.length, visible: visible_addons.length,
total: listed_addons.length total: listed_addons.length
}) }} }) }}
<template v-if="filter && filter.length"> <template v-if="filter">
{{ t('addon.displaying.filtered', 'The visible add-ons are being filtered by your search. Clear it to view all available add-ons.') }} {{ t('addon.displaying.filtered', 'The visible add-ons are being filtered by your search. Clear it to view all available add-ons.') }}
</template> </template>
</div> </div>
@ -222,10 +223,14 @@ export default {
if ( this.filter_enabled && ! enabled ) if ( this.filter_enabled && ! enabled )
return false; return false;
if ( ! this.filter || ! this.filter.length ) if ( this.filter ) {
return true; if ( this.filter.query ) {
if ( ! addon.search_terms || ! addon.search_terms.includes(this.filter.query) )
return false;
}
}
return addon.search_terms.includes(this.filter) return true;
}, },
onAdded() { onAdded() {
@ -242,6 +247,10 @@ export default {
navigate(...args) { navigate(...args) {
this.$emit('navigate', ...args); this.$emit('navigate', ...args);
},
changeItem(...args) {
this.$emit('change-item', ...args);
} }
} }
} }

View file

@ -145,6 +145,18 @@
</span> </span>
</button> </button>
</template> </template>
<button
v-if="! external"
class="tw-button ffz-button--hollow tw-mg-r-1"
@click="openChangelog"
>
<span class="tw-button__icon tw-button__icon--left">
<figure class="ffz-i-doc-text" />
</span>
<span class="tw-button__text">
{{ t('addon.changelog', 'Changelog') }}
</span>
</button>
<a <a
v-if="addon.website" v-if="addon.website"
:href="addon.website" :href="addon.website"
@ -285,6 +297,16 @@ export default {
this.$emit('navigate', ...list); this.$emit('navigate', ...list);
}, },
openChangelog() {
const list = [`add_ons.changelog.${this.id}`];
if ( this.addon.short_name )
list.push(`add_ons.changelog.${this.addon.short_name.toSnakeCase()}`);
if ( this.addon.name )
list.push(`add_ons.changelog.${this.addon.name.toSnakeCase()}`);
this.$emit('navigate', ...list);
},
reloadAddon() { reloadAddon() {
this.reloading = true; this.reloading = true;
this.item.reloadAddon(this.id) this.item.reloadAddon(this.id)

View file

@ -1,12 +1,26 @@
<template lang="html"> <template lang="html">
<div class="ffz--changelog tw-border-t tw-pd-t-1"> <div class="ffz--changelog tw-border-t tw-pd-t-1">
<div class="tw-align-center"> <div class="tw-align-center">
<h2 v-if="addons"> <h3 v-if="addon" class="tw-mg-b-1 tw-flex tw-align-items-center tw-justify-content-center">
<figure
v-if="addon.icon"
class="ffz-avatar ffz-avatar--size-30 tw-mg-r-05"
>
<img
:src="addon.icon"
class="tw-block tw-image tw-image-avatar"
>
</figure>
{{ t('setting.add_ons.specific-changelog', '{name} Changelog', {
name: addon.name
}) }}
</h3>
<h3 v-else-if="addons" class="tw-mg-b-1">
{{ t('setting.add_ons.changelog.title', 'Add-Ons Changelog') }} {{ t('setting.add_ons.changelog.title', 'Add-Ons Changelog') }}
</h2> </h3>
<h2 v-else> <h3 v-else class="tw-mg-b-1">
{{ t('home.changelog', 'Changelog') }} {{ t('home.changelog', 'Changelog') }}
</h2> </h3>
</div> </div>
<div v-if=" ! addons" class="tw-mg-b-1 tw-flex tw-align-items-center"> <div v-if=" ! addons" class="tw-mg-b-1 tw-flex tw-align-items-center">
@ -38,10 +52,30 @@
<div v-if="! addons && commit.active" class="ffz-pill tw-mg-r-05"> <div v-if="! addons && commit.active" class="ffz-pill tw-mg-r-05">
{{ t('home.changelog.current', 'Current Version') }} {{ t('home.changelog.current', 'Current Version') }}
</div> </div>
<div v-if="commit.title" class="tw-font-size-4"> <figure
v-if="commit.icon"
class="ffz-avatar ffz-avatar--size-20 tw-mg-r-05"
>
<img
:src="commit.icon"
class="tw-block tw-image tw-image-avatar"
>
</figure>
<a
v-if="commit.title && commit.title_nav"
class="tw-font-size-5 ffz-link ffz-link--inherit"
href="#"
@click.prevent="titleNav(commit.title_nav)"
>
{{ commit.title }}
</a>
<div v-else-if="commit.title" class="tw-font-size-5">
{{ commit.title }} {{ commit.title }}
</div> </div>
<div v-if="commit.author"> <div v-if="commit.version" class="tw-font-size-4 tw-mg-l-05">
<span class="tw-c-text-alt-2">v</span>{{ commit.version }}
</div>
<div v-if="commit.author" class="tw-mg-l-05">
<t-list <t-list
phrase="home.changelog.by-line" phrase="home.changelog.by-line"
default="By: {user}" default="By: {user}"
@ -86,6 +120,10 @@
>({{ formatDate(commit.date) }})</time> >({{ formatDate(commit.date) }})</time>
</div> </div>
<markdown :source="commit.message" /> <markdown :source="commit.message" />
<div v-for="entry in commit.segments" class="ffz--changelog-segment">
<strong>{{ entry.key }}</strong>
<markdown :source="entry.value" />
</div>
</li> </li>
</ul> </ul>
@ -111,7 +149,21 @@
import {get} from 'utilities/object'; import {get} from 'utilities/object';
const TITLE_MATCH = /^v?(\d+\.\d+\.\d+(?:-[^\n]+)?)\n+/; const TITLE_MATCH = /^(.+?)?\s*v?(\d+\.\d+\.\d+(?:\-[a-z0-9-]+)?)$/i,
SETTING_REGEX = /\]\(~([^)]+)\)/g,
CHANGE_REGEX = /^\*\s*([^:]+?):\s*(.+)$/i,
ISSUE_REGEX = /(^|\s)#(\d+)\b/g;
function linkify(text, repo) {
text = text.replace(SETTING_REGEX, (_, link) => {
return `](~${link})`
});
return text.replace(ISSUE_REGEX, (_, space, number) => {
return `${space}[#${number}](https://github.com/FrankerFaceZ/${repo}/issues/${number})`;
});
}
export default { export default {
@ -120,6 +172,7 @@ export default {
data() { data() {
return { return {
error: false, error: false,
addon: this.item.addon,
addons: this.item.addons, addons: this.item.addons,
nonversioned: false, nonversioned: false,
loading: false, loading: false,
@ -130,35 +183,130 @@ export default {
computed: { computed: {
display() { display() {
window.thing = this;
const out = [], const out = [],
addons = this.addons ? this.item.getFFZ().resolve('addons') : null,
old_commit = this.t('home.changelog.nonversioned', 'Non-Versioned Commit'); old_commit = this.t('home.changelog.nonversioned', 'Non-Versioned Commit');
for(const commit of this.commits) { for(const commit of this.commits) {
let message = commit.commit.message, const input = commit.commit.message;
let title = old_commit,
title_nav = null,
icon = null,
version = null,
author = null, author = null,
title = old_commit; sections = {},
description = [];
if ( this.addons ) { if ( /\bskiplog\b/i.test(input) && ! this.nonversion )
title = null; continue;
author = commit.author;
} else { const lines = input.split(/\r?\n/),
const match = TITLE_MATCH.exec(message); first = lines.shift(),
match = first ? TITLE_MATCH.exec(first) : null;
if ( match ) {
title = match[1];
message = message.slice(match[0].length);
} else if ( ! this.nonversioned )
continue;
}
const date = new Date(commit.commit.author.date), const date = new Date(commit.commit.author.date),
active = commit.sha === window.FrankerFaceZ.version_info.commit; active = commit.sha === window.FrankerFaceZ.version_info.commit,
has_content = lines.length && match;
if ( ! this.nonversion && ! has_content )
continue;
let last_bit = null;
if ( match ) {
title = match[1];
version = match[2];
}
if ( has_content )
for(const line of lines) {
const trimmed = line.trim();
if ( ! trimmed.length ) {
if ( ! last_bit && description.length )
description.push(line);
continue;
}
const m = CHANGE_REGEX.exec(trimmed);
if ( ! m ) {
if ( ! last_bit )
description.push(line);
else
last_bit.push(trimmed);
} else {
const section = sections[m[1]] = sections[m[1]] || [];
last_bit = [m[2]];
section.push(last_bit);
}
}
else {
lines.unshift(first);
description = lines;
}
let message = description.join('\n').trim();
const segments = [];
for(const [key, val] of Object.entries(sections)) {
if ( ! val?.length )
continue;
const bit = val.map(x => `* ${x.join(' ')}`).join('\n').trim();
segments.push({
key,
value: linkify(bit, this.addons ? 'add-ons' : 'frankerfacez')
});
}
if ( this.addons ) {
author = commit.author;
if ( title ) {
const ltitle = title.toLowerCase();
if ( addons?.addons )
for(const addon of Object.values(addons.addons)) {
if ((addon.short_name && addon.short_name.toLowerCase() === ltitle) ||
(addon.name && addon.name.toLowerCase() === ltitle) ||
(addon.id && addon.id.toLowerCase() === ltitle)
) {
icon = addon.icon;
title_nav = [`add_ons.changelog.${addon.id}`];
if ( addon.short_name )
title_nav.push(`add_ons.changelog.${addon.short_name.toSnakeCase()}`);
if ( addon.name )
title_nav.push(`add_ons.changelog.${addon.name.toSnakeCase()}`);
break;
}
}
// Default Icon
if ( ! icon )
icon = 'https://cdn.frankerfacez.com/badge/2/4/solid';
}
}
if ( this.addon ) {
icon = null;
title = null;
}
out.push({ out.push({
icon,
title, title,
title_nav,
version,
author, author,
message, message,
segments,
active, active,
hash: commit.sha && commit.sha.slice(0,7), hash: commit.sha && commit.sha.slice(0,7),
link: commit.html_url, link: commit.html_url,
@ -177,6 +325,11 @@ export default {
}, },
methods: { methods: {
titleNav(nav) {
if ( Array.isArray(nav) )
this.$emit('navigate', ...nav);
},
formatDate(value) { formatDate(value) {
if ( ! value ) if ( ! value )
return ''; return '';
@ -197,8 +350,15 @@ export default {
this.loading = true; this.loading = true;
const url = new URL(`https://api.github.com/repos/frankerfacez/${this.addons ? 'add-ons' : 'frankerfacez'}/commits`);
if ( until )
url.searchParams.append('until', until);
if ( this.addon )
url.searchParams.append('path', `src/${this.addon.id}`);
try { try {
const resp = await fetch(`https://api.github.com/repos/frankerfacez/${this.addons ? 'add-ons' : 'frankerfacez'}/commits${until ? `?until=${until}` : ''}`), const resp = await fetch(url),
data = resp.ok ? await resp.json() : null; data = resp.ok ? await resp.json() : null;
if ( ! data || ! Array.isArray(data) ) { if ( ! data || ! Array.isArray(data) ) {

View file

@ -252,12 +252,15 @@ export default {
}, },
visible_ffz() { visible_ffz() {
const items = this.sorted_ffz, let out = this.sorted_ffz;
f = this.filter && this.filter.toLowerCase();
if ( ! f )
return items;
return items.filter(x => matches(x, f)); if ( this.filter?.query )
out = out.filter(x => matches(x, this.filter.query));
if ( this.filter?.flags && this.filter.flags.has('modified') )
out = out.filter(x => this.item.hasOverride(x.key));
return out;
}, },
sorted_twitch() { sorted_twitch() {
@ -265,12 +268,15 @@ export default {
}, },
visible_twitch() { visible_twitch() {
const items = this.sorted_twitch, let out = this.sorted_twitch;
f = this.filter && this.filter.toLowerCase();
if ( ! f )
return items;
return items.filter(x => matches(x, f)); if ( this.filter?.query )
out = out.filter(x => matches(x, this.filter.query));
if ( this.filter?.flags && this.filter.flags.has('modified') )
out = out.filter(x => this.item.hasTwitchOverride(x.key));
return out;
} }
}, },

View file

@ -79,6 +79,7 @@
:current-item="currentItem" :current-item="currentItem"
:modal="nav" :modal="nav"
:filter="filter" :filter="filter"
:context="context"
@change-item="changeItem" @change-item="changeItem"
@mark-seen="markSeen" @mark-seen="markSeen"
@mark-expanded="markExpanded" @mark-expanded="markExpanded"
@ -128,6 +129,10 @@
import displace from 'displacejs'; import displace from 'displacejs';
import { getDialogNextZ } from 'src/utilities/dialog'; import { getDialogNextZ } from 'src/utilities/dialog';
const VALID_FLAGS = [
'modified'
];
export default { export default {
data() { data() {
const out = this.$vnode.data; const out = this.$vnode.data;
@ -139,7 +144,32 @@ export default {
computed: { computed: {
filter() { filter() {
return this.query.toLowerCase() let query = this.query.toLowerCase();
let flags = new Set;
query = query.replace(/(?<=^|\s)@(\S+)(?:\s+|$)/g, (match, flag, index) => {
if ( VALID_FLAGS.includes(flag) ) {
flags.add(flag);
return '';
}
return match;
});
query = query.trim();
if ( ! query.length )
query = null;
if ( ! flags.size )
flags = null;
if ( ! query && ! flags )
return null;
return {
flags,
query
}
} }
}, },

View file

@ -57,11 +57,32 @@ export default {
this.$emit('navigate', ...args); this.$emit('navigate', ...args);
}, },
shouldShow(item) { shouldShow(item, is_walking = false) {
if ( ! this.filter || ! this.filter.length || ! item.search_terms ) if ( ! this.filter || item.no_filter )
return true; return true;
return item.search_terms.includes(this.filter); if ( this.filter.flags ) {
if ( this.filter.flags.has('modified') ) {
// We need to tree walk for this one.
if ( ! is_walking ) {
for(const key of ['tabs', 'contents', 'items'])
if ( item[key] )
for(const thing of item[key])
if ( this.shouldShow(thing) )
return true;
}
if ( ! item.setting || ! this.context.currentProfile.has(item.setting) )
return false;
}
}
if ( this.filter.query ) {
if ( ! item.search_terms || ! item.search_terms.includes(this.filter.query) )
return false;
}
return true;
} }
} }
} }

View file

@ -149,15 +149,36 @@ export default {
}, },
methods: { methods: {
shouldShow(item) { shouldShow(item, is_walking = false) {
if ( ! this.filter || ! this.filter.length || ! item.search_terms ) if ( ! this.filter || item.no_filter )
return true; return true;
return item.no_filter || item.search_terms.includes(this.filter); if ( this.filter.flags ) {
if ( this.filter.flags.has('modified') ) {
// We need to tree walk for this one.
if ( ! is_walking ) {
for(const key of ['tabs', 'contents', 'items'])
if ( item[key] )
for(const thing of item[key])
if ( this.shouldShow(thing) )
return true;
}
if ( ! item.setting || ! this.context.currentProfile.has(item.setting) )
return false;
}
}
if ( this.filter.query ) {
if ( ! item.search_terms || ! item.search_terms.includes(this.filter.query) )
return false;
}
return true;
}, },
countMatches(item, seen) { countMatches(item, seen) {
if ( ! this.filter || ! this.filter.length || ! item ) if ( ! this.filter || ! item )
return 0; return 0;
if ( seen && seen.has(item) ) if ( seen && seen.has(item) )
@ -175,7 +196,7 @@ export default {
for(const thing of item[key]) for(const thing of item[key])
count += this.countMatches(thing, seen); count += this.countMatches(thing, seen);
if ( item.setting && item.search_terms && item.search_terms.includes(this.filter) ) if ( item.setting && this.shouldShow(item, true) )
count++; count++;
return count; return count;

View file

@ -13,7 +13,7 @@
<li <li
v-for="item in displayed" v-for="item in displayed"
:key="item.full_key" :key="item.full_key"
:class="[currentItem === item ? 'active' : '']" :class="[(currentItem === item || item.hide_children && containsCurrent(item)) ? 'active' : '']"
:data-key="item.full_key" :data-key="item.full_key"
role="presentation" role="presentation"
> >
@ -26,7 +26,7 @@
> >
<span <span
:class="[ :class="[
item.items ? '' : 'ffz--invisible', (item.items && ! item.hide_children) ? '' : 'ffz--invisible',
item.expanded ? 'ffz-i-down-dir' : 'ffz-i-right-dir' item.expanded ? 'ffz-i-down-dir' : 'ffz-i-right-dir'
]" ]"
role="presentation" role="presentation"
@ -46,10 +46,11 @@
</span> </span>
</div> </div>
<menu-tree <menu-tree
v-if="item.items && item.expanded" v-if="item.items && item.expanded && ! item.hide_children"
:root="item" :root="item"
:current-item="currentItem" :current-item="currentItem"
:modal="item.items" :modal="item.items"
:context="context"
:filter="filter" :filter="filter"
@change-item="i => $emit('change-item', i)" @change-item="i => $emit('change-item', i)"
@mark-seen="i => $emit('mark-seen', i)" @mark-seen="i => $emit('mark-seen', i)"
@ -97,7 +98,7 @@ function recursiveExpand(node, vue) {
export default { export default {
props: ['root', 'modal', 'currentItem', 'filter'], props: ['root', 'modal', 'currentItem', 'filter', 'context'],
computed: { computed: {
tabIndex() { tabIndex() {
@ -110,18 +111,36 @@ export default {
}, },
methods: { methods: {
shouldShow(item) { shouldShow(item, is_walking = false) {
if ( ! this.filter || ! this.filter.length || this.containsCurrent(item) ) if ( ! this.filter || this.containsCurrent(item) )
return true; return true;
if ( item.search_terms && item.search_terms.includes(this.filter) ) if ( this.filter.flags ) {
return true; if ( this.filter.flags.has('modified') ) {
// We need to tree walk for this one.
if ( ! is_walking ) {
for(const key of ['tabs', 'contents', 'items'])
if ( item[key] )
for(const thing of item[key])
if ( this.shouldShow(thing) )
return true;
}
return false; if ( ! item.setting || ! this.context.currentProfile.has(item.setting) )
return false;
}
}
if ( this.filter.query ) {
if ( ! item.search_terms || ! item.search_terms.includes(this.filter.query) )
return false;
}
return true;
}, },
countMatches(item, seen) { countMatches(item, seen) {
if ( ! this.filter || ! this.filter.length || ! item ) if ( ! this.filter || ! item )
return 0; return 0;
if ( seen && seen.has(item) ) if ( seen && seen.has(item) )
@ -139,7 +158,7 @@ export default {
for(const thing of item[key]) for(const thing of item[key])
count += this.countMatches(thing, seen); count += this.countMatches(thing, seen);
if ( item.setting && item.search_terms && item.search_terms.includes(this.filter) ) if ( item.setting && this.shouldShow(item, true) )
count++; count++;
return count; return count;

View file

@ -196,14 +196,16 @@ export default class MainMenu extends Module {
this.settings.addUI('changelog', { this.settings.addUI('changelog', {
path: 'Home > Changelog @{"profile_warning": false}', path: 'Home > Changelog @{"profile_warning": false}',
component: 'changelog' component: 'changelog',
getFFZ: () => this
}); });
this.settings.addUI('addon-changelog', { this.settings.addUI('addon-changelog', {
path: 'Add-Ons > Changelog @{"sort": -1000, "profile_warning": false}', path: 'Add-Ons > Changelog @{"sort": -1000, "profile_warning": false, "hide_children": true}',
component: 'changelog', component: 'changelog',
force_seen: true, force_seen: true,
addons: true addons: true,
getFFZ: () => this
}); });
this.settings.addUI('legal', { this.settings.addUI('legal', {

View file

@ -103,6 +103,16 @@
background: linear-gradient(90deg, var(--color-background-body) 60%, transparent) !important; background: linear-gradient(90deg, var(--color-background-body) 60%, transparent) !important;
} }
.directory-header-new__banner-cover {
background: linear-gradient(0deg,
var(--color-background-body),
color-mix(in srgb, var(--color-background-body) 25%, transparent)
), linear-gradient(90deg,
var(--color-background-body),
color-mix(in srgb, var(--color-background-body) 25%, transparent)
);
}
.emote-picker__emote-link { .emote-picker__emote-link {
&:hover { &:hover {
background-color: var(--color-background-button-text-hover) !important; background-color: var(--color-background-button-text-hover) !important;

View file

@ -119,7 +119,7 @@ export default {
methods: { methods: {
countMatches(item, seen) { countMatches(item, seen) {
if ( ! this.filter || ! this.filter.length || ! item ) if ( ! this.filter || ! item )
return 0; return 0;
if ( seen && seen.has(item) ) if ( seen && seen.has(item) )
@ -137,7 +137,7 @@ export default {
for(const thing of item[key]) for(const thing of item[key])
count += this.countMatches(thing, seen); count += this.countMatches(thing, seen);
if ( item.setting && item.search_terms && item.search_terms.includes(this.filter) ) if ( item.setting && this.shouldShow(item, true) )
count++; count++;
return count; return count;
@ -209,11 +209,32 @@ export default {
} }
}, },
shouldShow(item) { shouldShow(item, is_walking = false) {
if ( ! this.filter || ! this.filter.length || ! item.search_terms ) if ( ! this.filter || item.no_filter )
return true; return true;
return item.search_terms.includes(this.filter); if ( this.filter.flags ) {
if ( this.filter.flags.has('modified') ) {
// We need to tree walk for this one.
if ( ! is_walking ) {
for(const key of ['tabs', 'contents', 'items'])
if ( item[key] )
for(const thing of item[key])
if ( this.shouldShow(thing) )
return true;
}
if ( ! item.setting || ! this.context.currentProfile.has(item.setting) )
return false;
}
}
if ( this.filter.query ) {
if ( ! item.search_terms || ! item.search_terms.includes(this.filter.query) )
return false;
}
return true;
} }
} }
} }

View file

@ -397,6 +397,12 @@ textarea.ffz-input {
} }
} }
.ffz--changelog-segment {
ul {
list-style: disc;
}
}
.ffz--clear-settings code, .ffz--clear-settings code,
.ffz--experiments code { .ffz--experiments code {
user-select: none; user-select: none;