1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-05 05:40:54 +00:00
* Added: Accent Color customization, in `Appearance > Theme`.
* Added: Previews for custom date/time formats in `Appearance > Localization`.
* Changed: Assume Twitch clip and video thumbnails are Safe-For-Work.
* Fixed: Changelog dates not using i18n formatting. (Closes #873)
* Fixed: Icon picker string not localizing properly.
* Removed `Gray (no Purple)` legacy CSS. It's been breaking more and more over time, and it's not necessary at all with the new Accent Color setting.
This commit is contained in:
SirStendec 2020-08-05 19:23:18 -04:00
parent 6310a2ed49
commit a5e2dd9ef2
10 changed files with 198 additions and 114 deletions

View file

@ -1,7 +1,7 @@
{
"name": "frankerfacez",
"author": "Dan Salvato LLC",
"version": "4.20.22",
"version": "4.20.23",
"description": "FrankerFaceZ is a Twitch enhancement suite.",
"license": "Apache-2.0",
"scripts": {

View file

@ -180,8 +180,13 @@ export class TranslationManager extends Module {
ui: {
path: 'Appearance > Localization >> Formatting',
title: 'Date Format',
description: 'The default date format. Custom date formats are formated using the [Day.js](https://github.com/iamkun/dayjs#readme) library.',
description: 'The default date format. Custom date formats are formated using the [Day.js](https://day.js.org/docs/en/display/format) library.',
component: 'setting-combo-box',
extra: {
before: true,
mode: 'date',
component: 'format-preview'
},
data: () => {
const out = [], now = new Date;
for (const [key,fmt] of Object.entries(this._.formats.date)) {
@ -205,8 +210,13 @@ export class TranslationManager extends Module {
ui: {
path: 'Appearance > Localization >> Formatting',
title: 'Time Format',
description: 'The default time format. Custom time formats are formated using the [Day.js](https://github.com/iamkun/dayjs#readme) library.',
description: 'The default time format. Custom time formats are formated using the [Day.js](https://day.js.org/docs/en/display/format) library.',
component: 'setting-combo-box',
extra: {
before: true,
mode: 'time',
component: 'format-preview'
},
data: () => {
const out = [], now = new Date;
for (const [key,fmt] of Object.entries(this._.formats.time)) {
@ -230,8 +240,13 @@ export class TranslationManager extends Module {
ui: {
path: 'Appearance > Localization >> Formatting',
title: 'Date-Time Format',
description: 'The default combined date-time format. Custom time formats are formated using the [Day.js](https://github.com/iamkun/dayjs#readme) library.',
description: 'The default combined date-time format. Custom time formats are formated using the [Day.js](https://day.js.org/docs/en/display/format) library.',
component: 'setting-combo-box',
extra: {
before: true,
mode: 'datetime',
component: 'format-preview'
},
data: () => {
const out = [], now = new Date;
for (const [key,fmt] of Object.entries(this._.formats.datetime)) {

View file

@ -261,7 +261,7 @@ export const Clips = {
short: {
type: 'header',
image: {type: 'image', url: clip.thumbnailURL, sfw: false, aspect: 16/9},
image: {type: 'image', url: clip.thumbnailURL, sfw: true, aspect: 16/9},
title: clip.title,
subtitle,
extra
@ -332,7 +332,7 @@ export const Videos = {
url: token.url,
short: {
type: 'header',
image: {type: 'image', url: video.previewThumbnailURL, sfw: false, aspect: 16/9},
image: {type: 'image', url: video.previewThumbnailURL, sfw: true, aspect: 16/9},
title: video.title,
subtitle,
extra

View file

@ -186,9 +186,9 @@ export default {
is_today = date.toDateString() === today.toDateString();
if ( is_today )
return date.toLocaleTimeString();
return this.tTime(date);
return date.toLocaleDateString();
return this.tDate(date);
},
async fetchMore() {

View file

@ -0,0 +1,40 @@
<template>
<div class="tw-flex tw-align-items-start">
<label />
<div class="tw-c-text-alt tw-mg-b-05 tw-mg-x-05">
<code v-if="mode === 'date'">
{{ tDate(now, value) }}
</code>
<code v-else-if="mode === 'time'">
{{ tTime(now, value) }}
</code>
<code v-else>
{{ tDateTime(now, value) }}
</code>
</div>
</div>
</template>
<script>
export default {
props: ['context', 'item', 'value'],
data() {
return {
mode: this.item.extra.mode,
now: null
}
},
mounted() {
this.now = new Date;
this.timer = setInterval(() => this.now = new Date, 1000);
},
beforeDestroy() {
clearInterval(this.timer);
this.timer = null;
}
}
</script>

View file

@ -62,14 +62,18 @@
</button>
</div>
<section v-if="item.extra && item.extra.component && item.extra.before">
<component :is="item.extra.component" :context="context" :item="item" :value="value" />
</section>
<section
v-if="item.description"
class="tw-c-text-alt-2"
>
<markdown :source="t(item.desc_i18n_key || `${item.i18n_key}.description`, item.description)" />
</section>
<section v-if="item.extra">
<component :is="item.extra.component" :context="context" :item="item" />
<section v-if="item.extra && item.extra.component && ! item.extra.before">
<component :is="item.extra.component" :context="context" :item="item" :value="value" />
</section>
</div>
</template>

View file

@ -1,89 +0,0 @@
<template>
<div>
<error-tab v-if="errored" />
<loading-tab v-else-if="loading" />
<template v-else>
<ul>
<li
v-for="(entry, idx) in data"
:key="entry[1]"
:class="idx === 0 ? '' : 'tw-border-t'"
class="tw-pd-x-1 tw-pd-y-05"
>
<span
:data-title="fullTime(entry[0])"
data-tooltip-type="text"
class="ffz-tooltip tw-pd-r-1"
>{{ formatTime(entry[0]) }}: </span>
{{ entry[1] }}
</li>
</ul>
</template>
</div>
</template>
<script>
import LoadingTab from './loading-tab.vue';
import ErrorTab from './error-tab.vue';
export default {
components: {
'loading-tab': LoadingTab,
'error-tab': ErrorTab
},
props: ['tab', 'channel', 'user', 'self', 'getFFZ'],
data() {
return {
loading: true,
errored: false,
data: null
}
},
mounted() {
const socket = this.getFFZ().resolve('socket');
if ( ! socket ) {
this.errored = true;
return;
}
socket.call('get_name_history', this.user.login).then(data => {
this.loading = false;
if ( Array.isArray(data) )
data = data.reverse();
this.data = data;
}).catch(err => {
this.getFFZ().log.error('Error loading name history.', err);
this.errored = true;
})
},
methods: {
fullTime(time) {
try {
const date = new Date(time);
return date.toLocaleString();
} catch(err) {
return 'Unknown'
}
},
formatTime(time) {
try {
const date = new Date(time);
return date.toLocaleDateString();
} catch(err) {
return 'Unknown'
}
}
}
}
</script>

View file

@ -8,11 +8,24 @@ import Module from 'utilities/module';
import {createElement} from 'utilities/dom';
import {Color} from 'utilities/color';
import THEME_CSS from 'site/styles/theme.scss';
//import THEME_CSS from 'site/styles/theme.scss';
import NORMALIZER_CSS_URL from 'site/styles/color_normalizer.scss';
const BAD_ROUTES = ['product', 'prime', 'turbo'];
const COLORS = [
-.4, -.35, -.3, -.25, -.2, -.15, -.1, -.05, // 1-8
0, // 9
.05, .1, .15, .2, .25, .3 // 10-15
];
const ACCENT_COLORS = {
dark: {'c':{'accent': 9,'background-accent':8,'background-accent-alt':7,'background-accent-alt-2':6,'background-button':7,'background-button-active':7,'background-button-focus':8,'background-button-hover':8,'background-button-primary-active':7,'background-button-primary-default':9,'background-button-primary-hover':8,'background-graph':2,'background-graph-fill':8,'background-input-checkbox-checked':9,'background-input-checked':8,'background-interactable-active':9,'background-interactable-hover':8,'background-progress-countdown-status':9,'background-progress-status':9,'background-range-fill':9,'background-subscriber-stream-tag-active':4,'background-subscriber-stream-tag-default':4,'background-subscriber-stream-tag-hover':3,'background-toggle-checked':9,/*'background-tooltip':1,*/'background-top-nav':6,'border-brand':9,'border-button':7,'border-button-active':8,'border-button-focus':9,'border-button-hover':8,'border-input-checkbox-checked':9,'border-input-checkbox-focus':9,'border-input-focus':9,'border-interactable-selected':10,'border-subscriber-stream-tag':5,'border-tab-active':11,'border-tab-focus':11,'border-tab-hover':11,'border-toggle-focus':7,'border-toggle-hover':7,'border-whisper-incoming':10/*,'fill-brand':9*/,'text-button-text':8,'text-button-text-focus':'o1','text-button-text-hover':'o1','text-link':10,'text-link-active':10,'text-link-focus':10,'text-link-hover':10,'text-link-visited':10,'text-overlay-link-active':13,'text-overlay-link-focus':13,'text-overlay-link-hover':13,'text-tab-active':11,'background-chat':1,'background-chat-alt':3,'background-chat-header':2,'background-modal':3,'text-button-text-active':'o2'/*,'text-tooltip':1*/},'s':{'button-active':[8,'0 0 6px 0',''],'button-focus':[8,'0 0 6px 0',''],'input-focus':[8,'0 0 10px -2px',''],'interactable-focus':[8,'0 0 6px 0',''],'tab-focus':[11,'0 4px 6px -4px',''],'input':[5,'inset 0 0 0 1px','']}},
light: {'c':{'accent': 9,'background-accent':8,'background-accent-alt':7,'background-accent-alt-2':6,'background-button':7,'background-button-active':7,'background-button-focus':8,'background-button-hover':8,'background-button-primary-active':7,'background-button-primary-default':9,'background-button-primary-hover':8,'background-graph':15,'background-graph-fill':9,'background-input-checkbox-checked':9,'background-input-checked':8,'background-interactable-active':9,'background-interactable-hover':8,'background-progress-countdown-status':8,'background-progress-status':8,'background-range-fill':9,'background-subscriber-stream-tag-active':13,'background-subscriber-stream-tag-default':13,'background-subscriber-stream-tag-hover':14,'background-toggle-checked':9,/*'background-tooltip':1,*/'background-top-nav':7,'border-brand':9,'border-button':7,'border-button-active':8,'border-button-focus':9,'border-button-hover':8,'border-input-checkbox-checked':9,'border-input-checkbox-focus':9,'border-input-focus':9,'border-interactable-selected':9,'border-subscriber-stream-tag':10,'border-tab-active':8,'border-tab-focus':8,'border-tab-hover':8,'border-toggle-focus':8,'border-toggle-hover':8,'border-whisper-incoming':10/*,'fill-brand':9*/,'text-button-text':8,'text-button-text-focus':'o1','text-button-text-hover':'o1','text-link':8,'text-link-active':9,'text-link-focus':9,'text-link-hover':9,'text-link-visited':9,'text-overlay-link-active':13,'text-overlay-link-focus':13,'text-overlay-link-hover':13,'text-tab-active':8},'s':{'button-active':[8,'0 0 6px 0',''],'button-focus':[8,'0 0 6px 0',''],'input-focus':[10,'0 0 10px -2px',''],'interactable-focus':[8,'0 0 6px 1px',''],'tab-focus':[8,'0 4px 6px -4px','']}},
accent_dark: {'c':{'accent-hover':10,'accent':9,'accent-primary-1':1,'accent-primary-2':5,'accent-primary-3':6,'accent-primary-4':7,'accent-primary-5':8},'s':{}},
accent_light: {'c':{'accent-hover':10,'accent':9,'accent-primary-1':1,'accent-primary-2':5,'accent-primary-3':6,'accent-primary-4':7,'accent-primary-5':8},'s':{}}
};
export default class ThemeEngine extends Module {
constructor(...args) {
@ -37,6 +50,7 @@ export default class ThemeEngine extends Module {
title: 'Background',
description: 'Try `#0E0C13` for something close to the old dark theme, or `#0E0E0E` for a nice dark gray. Transparent colors not allowed.',
component: 'setting-color-box',
sort: 0,
alpha: false
},
changed: () => this.updateCSS()
@ -48,7 +62,21 @@ export default class ThemeEngine extends Module {
path: 'Appearance > Theme >> Colors',
title: 'Text',
description: 'If not set, this will automatically be set to white or black based on the brightness of the background.',
component: 'setting-color-box'
component: 'setting-color-box',
sort: 1
},
changed: () => this.updateCSS()
});
this.settings.add('theme.color.accent', {
default: '',
ui: {
path: 'Appearance > Theme >> Colors',
title: 'Accent',
description: 'The accent color is used for buttons, links, etc.',
component: 'setting-color-box',
alpha: false,
sort: 2
},
changed: () => this.updateCSS()
});
@ -60,7 +88,8 @@ export default class ThemeEngine extends Module {
title: 'Tooltip Background',
description: 'If not set, the tooltip settings will be automatically adjusted based on the brightness of the background.',
component: 'setting-color-box',
alpha: true
alpha: true,
sort: 10
},
changed: () => this.updateCSS()
});
@ -70,12 +99,13 @@ export default class ThemeEngine extends Module {
ui: {
path: 'Appearance > Theme >> Colors',
title: 'Tooltip Text',
component: 'setting-color-box'
component: 'setting-color-box',
sort: 11
},
changed: () => this.updateCSS()
});
this.settings.add('theme.dark', {
/*this.settings.add('theme.dark', {
requires: ['theme.is-dark'],
default: false,
process(ctx, val) {
@ -93,7 +123,7 @@ The CSS loaded by this setting is far too heavy and can cause performance issues
},
changed: val => this.updateSetting(val)
});
});*/
this.settings.add('theme.can-dark', {
requires: ['context.route.name'],
@ -128,7 +158,7 @@ The CSS loaded by this setting is far too heavy and can cause performance issues
this._normalizer = null;
}
updateOldCSS() {
/*updateOldCSS() {
const dark = this.settings.get('theme.is-dark'),
gray = this.settings.get('theme.dark');
@ -136,19 +166,22 @@ The CSS loaded by this setting is far too heavy and can cause performance issues
document.body.classList.toggle('tw-root--theme-ffz', gray);
this.css_tweaks.setVariable('border-color', dark ? (gray ? '#2a2a2a' : '#2c2541') : '#dad8de');
}
}*/
updateCSS() {
this.updateOldCSS();
//this.updateOldCSS();
this.css_tweaks.setVariable('border-color', 'var(--color-border-base)');
if ( ! this.settings.get('theme.can-dark') ) {
this.toggleNormalizer(false);
this.toggleAccentNormal(true);
this.css_tweaks.delete('colors');
return;
}
let dark = this.settings.get('theme.is-dark');
const bits = [];
const bits = [], accent_bits = [];
const background = Color.RGBA.fromCSS(this.settings.get('theme.color.background'));
if ( background ) {
@ -198,6 +231,46 @@ The CSS loaded by this setting is far too heavy and can cause performance issues
bits.push(`--color-text-alt-2: ${hsla._a(alpha - 0.4).toCSS()};`);
}
// Accent
const accent = Color.RGBA.fromCSS(this.settings.get('theme.color.accent'));
this.toggleAccentNormal(! accent);
if ( accent ) {
accent.a = 1;
const hsla = accent.toHSLA(),
luma = hsla.l;
const colors = COLORS.map(x => {
if ( x === 0 )
return accent.toCSS();
return hsla._l(luma + x).toCSS()
});
for(let i=0; i < colors.length; i++) {
bits.push(`--ffz-color-accent-${i+1}:${colors[i]};`);
}
let source = dark ? ACCENT_COLORS.dark : ACCENT_COLORS.light;
for(const [key,val] of Object.entries(source.c)) {
if ( typeof val !== 'number' )
continue;
bits.push(`--color-${key}:${colors[val-1]};`);
}
source = dark ? ACCENT_COLORS.accent_dark : ACCENT_COLORS.accent_light;
for(const [key,val] of Object.entries(source.c)) {
if ( typeof val !== 'number' )
continue;
accent_bits.push(`--color-${key}:${colors[val-1]} !important;`);
}
}
// Tooltips
let tooltip_bg = Color.RGBA.fromCSS(this.settings.get('theme.color.tooltip.background')),
tooltip_dark;
@ -231,7 +304,7 @@ The CSS loaded by this setting is far too heavy and can cause performance issues
}
if ( bits.length ) {
this.css_tweaks.set('colors', `body {${bits.join('\n')}}`);
this.css_tweaks.set('colors', `body {${bits.join('\n')}}.channel-info-content .tw-accent-region{${accent_bits.join('\n')}}`);
this.toggleNormalizer(true);
} else {
this.css_tweaks.delete('colors');
@ -239,6 +312,17 @@ The CSS loaded by this setting is far too heavy and can cause performance issues
}
}
toggleAccentNormal(enable) {
if ( enable ) {
const bits = [];
for(let i=0; i < 15; i++)
bits.push(`--ffz-color-accent-${i+1}:var(--color-twitch-purple-${i+1});`);
this.css_tweaks.set('accent-normal', `body {${bits.join('\n')}}`);
} else
this.css_tweaks.delete('accent-normal');
}
toggleNormalizer(enable) {
if ( ! this._normalizer ) {
if ( ! enable )
@ -258,7 +342,7 @@ The CSS loaded by this setting is far too heavy and can cause performance issues
document.head.appendChild(this._normalizer)
}
toggleStyle(enable) {
/*toggleStyle(enable) {
if ( ! this._style ) {
if ( ! enable )
return;
@ -280,10 +364,10 @@ The CSS loaded by this setting is far too heavy and can cause performance issues
updateSetting(enable) {
this.toggleStyle(enable);
this.updateCSS();
}
}*/
onEnable() {
this.updateSetting(this.settings.get('theme.dark'));
//this.updateSetting(this.settings.get('theme.dark'));
this.updateCSS();
}
}

View file

@ -36,3 +36,33 @@
color: var(--color-text-alt-2) !important;
}
}
html {
.navigation-link.active, .navigation-link:hover {
color: var(--ffz-color-accent-9) !important;
}
.search-result-verified-badge,
.search-item__history {
color: var(--ffz-color-accent-8) !important;
}
.navigation-link__active-indicator {
background-color: var(--ffz-color-accent-8) !important;
}
}
.tw-root--theme-dark, .dark-theme {
.navigation-link.active, .navigation-link:hover {
color: var(--ffz-color-accent-10) !important;
}
.search-result-verified-badge,
.search-item__history {
color: var(--ffz-color-accent-10) !important;
}
.navigation-link__active-indicator {
color: var(--ffz-color-accent-10) !important;
}
}

View file

@ -9,7 +9,7 @@
<input
:id="'icon-search$' + id"
ref="input"
:placeholder="('setting.icon.search', 'Search for Icon')"
:placeholder="t('setting.icon.search', 'Search for Icon')"
:value="isOpen ? search : val"
:class="[clearable ? 'tw-pd-r-5' : 'tw-pd-r-1']"
type="text"