mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.38.0
* Added: Options to hide the "Chat Highlight Settings" and "Shield Mode" buttons in the chat input. * Added: Option to fade the video player when paused or buffering. (Closes #1289) * Added: Message Hover chat actions! Now you can add custom actions in the style of Twitch's native "Pin Message" and "Reply to Message" buttons. This change also adds default actions for those that behave similarly to Twitch's native behavior. (Note: The Pin Message action is, by default, only visible if you have your mod icons displayed.) (Closes #1284. Closes #1293.) * Changed: Remove the Reply action from the defaults for In-Line chat actions. * Fixed: Duplicate words in *in* certain localized strings with human friendly relative times. (Closes #1292) * Fixed: Bug where the link testing debug component would not collect its event source when being destroyed. * Fixed: Popup UI elements not appearing with the correct colors. (Closes #1285) * Fixed: Elements in the FFZ Control Center sometimes failing to display scroll-bars correctly after a Twitch update. * Fixed: The mouse cursor not hiding correctly when positioned over the player with controls not visible. * Fixed: Tab-completion sometimes failing to include emotes from add-ons due to improperly cached data. (Closes #1299. Thanks cfinegan) * Fixed: Rich token rendering not setting alt text or width and height on images.
This commit is contained in:
parent
9017dd644f
commit
9ab9f69583
38 changed files with 509 additions and 110 deletions
|
@ -785,6 +785,26 @@
|
|||
"css": "list-bullet",
|
||||
"code": 61642,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "7655b7161cf9660beeb1af4036db1198",
|
||||
"css": "mastodon",
|
||||
"code": 59463,
|
||||
"src": "custom_icons",
|
||||
"selected": true,
|
||||
"svg": {
|
||||
"path": "M932.9 220.7C918.5 114.6 825 31 714.2 14.8 695.5 12 624.7 2.1 460.6 2.1H459.4C295.3 2.1 260.1 12 241.4 14.8 133.7 30.5 35.3 105.6 11.4 213 0 265.9-1.3 324.5 0.9 378.2 3.9 455.3 4.5 532.3 11.6 609.1 16.5 660.1 25 710.7 37.1 760.5 59.8 852.5 151.6 929 241.6 960.3 337.9 992.8 441.5 998.2 540.8 975.9 551.7 973.4 562.5 970.4 573.2 967.1 597.3 959.5 625.6 951.1 646.3 936.2 646.6 935.9 646.9 935.7 647 935.4 647.2 935.1 647.3 934.7 647.3 934.4V860C647.3 859.6 647.2 859.3 647.1 859 646.9 858.7 646.7 858.5 646.4 858.3 646.2 858.1 645.9 857.9 645.6 857.8 645.2 857.8 644.9 857.8 644.6 857.8 581 872.9 515.8 880.4 450.4 880.3 337.9 880.3 307.6 827.5 299 805.5 292 786.5 287.6 766.7 285.8 746.5 285.8 746.2 285.9 745.8 286 745.5 286.1 745.2 286.3 744.9 286.6 744.7 286.9 744.5 287.2 744.4 287.5 744.3 287.9 744.2 288.2 744.2 288.5 744.3 351.1 759.2 415.2 766.8 479.5 766.8 495 766.8 510.4 766.8 525.9 766.3 590.6 764.6 658.8 761.3 722.4 749 724 748.7 725.6 748.4 727 748 827.4 728.9 922.9 669.1 932.7 517.5 933 511.5 933.9 455 933.9 448.8 934 427.7 940.8 299.5 932.9 220.7ZM778.4 598.9H672.8V343.1C672.8 289.3 650.1 261.8 604 261.8 553.2 261.8 527.8 294.3 527.8 358.5V498.5H422.9V358.5C422.9 294.3 397.4 261.8 346.7 261.8 300.8 261.8 277.9 289.3 277.9 343.1V598.9H172.4V335.4C172.4 281.5 186.3 238.7 214.1 207 242.7 175.4 280.4 159.1 327.1 159.1 381.1 159.1 421.9 179.7 449.2 220.8L475.4 264.4 501.7 220.8C529 179.7 569.8 159.1 623.8 159.1 670.4 159.1 708 175.4 736.8 207 764.6 238.7 778.5 281.5 778.5 335.4L778.4 598.9Z",
|
||||
"width": 937
|
||||
},
|
||||
"search": [
|
||||
"logo-black"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "012ff5762ccb18c16bdfdd6baf187406",
|
||||
"css": "volume-up",
|
||||
"code": 59464,
|
||||
"src": "elusive"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.37.1",
|
||||
"version": "4.38.0",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
Binary file not shown.
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Copyright (C) 2021 by original authors @ fontello.com</metadata>
|
||||
<metadata>Copyright (C) 2022 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="ffz-fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="ffz-fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
|
@ -148,6 +148,10 @@
|
|||
|
||||
<glyph glyph-name="right-open" unicode="" d="M618 361l-414-415q-11-10-25-10t-25 10l-93 93q-11 11-11 25t11 25l296 297-296 296q-11 11-11 25t11 25l93 93q10 11 25 11t25-11l414-414q10-11 10-25t-10-25z" horiz-adv-x="714.3" />
|
||||
|
||||
<glyph glyph-name="mastodon" unicode="" d="M933 629c-14 106-108 190-219 206-18 3-89 13-253 13h-2c-164 0-199-10-218-13-107-15-206-91-230-198-11-53-12-111-10-165 3-77 4-154 11-231 5-51 13-102 25-151 23-93 115-169 205-200 96-33 200-38 299-16 11 3 22 6 32 9 24 7 53 16 73 31 1 0 1 0 1 1 0 0 0 0 0 1v74c0 0 0 1 0 1 0 0 0 0-1 1 0 0 0 0 0 0-1 0-1 0-1 0-64-15-129-22-195-22-112 0-142 53-151 75-7 19-11 38-13 59 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 1 1 1 0 0 0 0 1 0 62-15 126-23 191-23 15 0 30 0 46 1 65 1 133 5 196 17 2 0 4 1 5 1 100 19 196 79 206 231 0 6 1 62 1 68 0 21 7 150-1 228z m-155-378h-105v256c0 54-23 81-69 81-51 0-76-32-76-96v-140h-105v140c0 64-26 96-76 96-46 0-69-27-69-81v-256h-106v264c0 54 14 96 42 128 29 32 66 48 113 48 54 0 95-21 122-62l26-43 27 43c27 41 68 62 122 62 46 0 84-16 113-48 28-32 42-74 42-128l-1-264z" horiz-adv-x="937" />
|
||||
|
||||
<glyph glyph-name="volume-up" unicode="" d="M0 169l0 360 203 0 307 250 0-858-307 248-203 0z m563 33q62 63 62 151t-62 152l60 65q90-90 92-219 0-125-92-213z m101-105q106 105 106 256t-106 258l66 62q131-133 131-319t-131-321z m100-98q146 147 146 354t-146 353l62 65q82-82 128-190t46-227-46-229-128-190z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="move" unicode="" d="M1000 350q0-14-11-25l-142-143q-11-11-26-11t-25 11-10 25v72h-215v-215h72q14 0 25-10t11-25-11-25l-143-143q-10-11-25-11t-25 11l-143 143q-11 10-11 25t11 25 25 10h72v215h-215v-72q0-14-10-25t-25-11-25 11l-143 143q-11 11-11 25t11 25l143 143q10 11 25 11t25-11 10-25v-72h215v215h-72q-14 0-25 10t-11 25 11 26l143 142q11 11 25 11t25-11l143-142q11-11 11-26t-11-25-25-10h-72v-215h215v72q0 14 10 25t25 11 26-11l142-143q11-10 11-25z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="link-ext" unicode="" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
|
||||
|
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 44 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -67,6 +67,42 @@ export default class Actions extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.actions.hover', {
|
||||
process: (ctx, val) =>
|
||||
val.filter(x => x.appearance &&
|
||||
this.renderers[x.appearance.type] &&
|
||||
(! this.renderers[x.appearance.type].load || this.renderers[x.appearance.type].load(x.appearance)) &&
|
||||
(! x.action || this.actions[x.action])
|
||||
),
|
||||
|
||||
default: [
|
||||
{v: {action: 'pin', appearance: {type: 'icon', icon: 'ffz-i-pin'}, options: {}, display: {mod_icons: true}}},
|
||||
{v: {action: 'reply', appearance: {type: 'dynamic'}, options: {}, display: {}}}
|
||||
],
|
||||
|
||||
type: 'array_merge',
|
||||
inherit_default: true,
|
||||
|
||||
ui: {
|
||||
path: 'Chat > Actions > Message Hover @{"description": "Here, you can define custom actions that will appear on top of messages in chat when you hover over them. If you aren\'t seeing an action you\'ve defined here in chat, please make sure that you have enabled Mod Icons in the chat settings menu."}',
|
||||
component: 'chat-actions',
|
||||
context: ['user', 'room', 'message'],
|
||||
inline: true,
|
||||
modifiers: true,
|
||||
hover_modifier: false,
|
||||
|
||||
data: () => {
|
||||
const chat = this.resolve('site.chat');
|
||||
|
||||
return {
|
||||
color: val => chat && chat.colors ? chat.colors.process(val) : val,
|
||||
actions: deep_copy(this.actions),
|
||||
renderers: deep_copy(this.renderers)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.actions.inline', {
|
||||
// Filter out actions
|
||||
process: (ctx, val) =>
|
||||
|
@ -80,8 +116,7 @@ export default class Actions extends Module {
|
|||
{v: {action: 'ban', appearance: {type: 'icon', icon: 'ffz-i-block'}, options: {}, display: {mod: true, mod_icons: true, deleted: false}}},
|
||||
{v: {action: 'unban', appearance: {type: 'icon', icon: 'ffz-i-ok'}, options: {}, display: {mod: true, mod_icons: true, deleted: true}}},
|
||||
{v: {action: 'timeout', appearance: {type: 'icon', icon: 'ffz-i-clock'}, display: {mod: true, mod_icons: true}}},
|
||||
{v: {action: 'msg_delete', appearance: {type: 'icon', icon: 'ffz-i-trash'}, options: {}, display: {mod: true, mod_icons: true}}},
|
||||
{v: {action: 'reply', appearance: {type: 'icon', icon: 'ffz-i-reply'}, options: {}, display: {}}}
|
||||
{v: {action: 'msg_delete', appearance: {type: 'icon', icon: 'ffz-i-trash'}, options: {}, display: {mod: true, mod_icons: true}}}
|
||||
],
|
||||
|
||||
type: 'array_merge',
|
||||
|
@ -501,6 +536,8 @@ export default class Actions extends Module {
|
|||
if ( ! data )
|
||||
continue;
|
||||
|
||||
data.ctx = 'room';
|
||||
|
||||
const type = data.type;
|
||||
if ( type ) {
|
||||
if ( type === 'new-line' ) {
|
||||
|
@ -545,6 +582,12 @@ export default class Actions extends Module {
|
|||
if ( maybe_call(act.hidden, this, data, null, current_room, current_user, mod_icons) )
|
||||
continue;
|
||||
|
||||
if ( ap.type === 'dynamic' ) {
|
||||
const out = act.dynamicAppearance && act.dynamicAppearance.call(this, Object.assign({}, ap), data, null, current_room, current_user, mod_icons);
|
||||
if ( out )
|
||||
ap = out;
|
||||
}
|
||||
|
||||
if ( act.override_appearance ) {
|
||||
const out = act.override_appearance.call(this, Object.assign({}, ap), data, null, current_room, current_user, mod_icons);
|
||||
if ( out )
|
||||
|
@ -649,6 +692,8 @@ export default class Actions extends Module {
|
|||
if ( ! data )
|
||||
continue;
|
||||
|
||||
data.ctx = 'user_context';
|
||||
|
||||
if ( data.type === 'new-line' ) {
|
||||
line = null;
|
||||
continue;
|
||||
|
@ -684,6 +729,12 @@ export default class Actions extends Module {
|
|||
if ( maybe_call(act.hidden, this, data, msg, r, u, mod_icons) )
|
||||
continue;
|
||||
|
||||
if ( ap.type === 'dynamic' ) {
|
||||
const out = act.dynamicAppearance && act.dynamicAppearance.call(this, Object.assign({}, ap), data, msg, r, u, mod_icons);
|
||||
if ( out )
|
||||
ap = out;
|
||||
}
|
||||
|
||||
if ( act.override_appearance ) {
|
||||
const out = act.override_appearance.call(this, Object.assign({}, ap), data, msg, r, u, mod_icons);
|
||||
if ( out )
|
||||
|
@ -704,7 +755,7 @@ export default class Actions extends Module {
|
|||
|
||||
const btn = (<button
|
||||
class={`ffz-tooltip ffz-tooltip--no-mouse tw-button tw-button--text${disabled ? ' tw-button--disabled disabled' : ''}`}
|
||||
disabled={disabled}
|
||||
//disabled={disabled}
|
||||
data-tooltip-type="action"
|
||||
data-action={data.action}
|
||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||
|
@ -743,6 +794,94 @@ export default class Actions extends Module {
|
|||
}
|
||||
|
||||
|
||||
renderHover(msg, mod_icons, current_user, current_room, createElement, instance = null) {
|
||||
const actions = [];
|
||||
|
||||
const current_level = this.getUserLevel(current_room, current_user),
|
||||
msg_level = this.getUserLevel(current_room, msg.user),
|
||||
is_self = msg.user && current_user && current_user.login === msg.user.login;
|
||||
|
||||
if ( current_level < 3 )
|
||||
mod_icons = false;
|
||||
|
||||
const chat = this.resolve('site.chat');
|
||||
|
||||
let had_action = false;
|
||||
|
||||
for(const data of this.parent.context.get('chat.actions.hover')) {
|
||||
if ( ! data.action || ! data.appearance )
|
||||
continue;
|
||||
|
||||
data.ctx = 'hover';
|
||||
|
||||
let ap = data.appearance || {};
|
||||
const disp = data.display || {},
|
||||
keys = disp.keys,
|
||||
act = this.actions[data.action];
|
||||
|
||||
if ( ! act || disp.disabled ||
|
||||
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
||||
(disp.mod != null && disp.mod !== (current_level > msg_level)) ||
|
||||
(disp.staff != null && disp.staff !== (current_user ? !!current_user.staff : false)) ||
|
||||
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
|
||||
continue;
|
||||
|
||||
if ( is_self && ! act.can_self )
|
||||
continue;
|
||||
|
||||
if ( maybe_call(act.hidden, this, data, msg, current_room, current_user, mod_icons, instance) )
|
||||
continue;
|
||||
|
||||
if ( ap.type === 'dynamic' ) {
|
||||
const out = act.dynamicAppearance && act.dynamicAppearance.call(this, Object.assign({}, ap), data, msg, current_room, current_user, mod_icons, instance);
|
||||
if ( out )
|
||||
ap = out;
|
||||
}
|
||||
|
||||
if ( act.override_appearance ) {
|
||||
const out = act.override_appearance.call(this, Object.assign({}, ap), data, msg, current_room, current_user, mod_icons, instance);
|
||||
if ( out )
|
||||
ap = out;
|
||||
}
|
||||
|
||||
const def = this.renderers[ap.type];
|
||||
if ( ! def )
|
||||
continue;
|
||||
|
||||
const has_color = def.colored && ap.color,
|
||||
disabled = maybe_call(act.disabled, this, data, msg, current_room, current_user, mod_icons, instance) || false,
|
||||
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
||||
contents = def.render.call(this, ap, createElement, color);
|
||||
|
||||
had_action = true;
|
||||
actions.push(<div class={`ffz-hover-action${keys ? ` ffz-has-modifier ffz-modifier-${keys}` : ''}`}>
|
||||
<button
|
||||
class={`ffz-tooltip ffz-mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}`}
|
||||
//disabled={disabled}
|
||||
data-tooltip-type="action"
|
||||
data-action={data.action}
|
||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||
data-tip={ap.tooltip}
|
||||
onClick={this.handleClick}
|
||||
onContextMenu={this.handleContext}
|
||||
>
|
||||
{contents}
|
||||
</button>
|
||||
</div>);
|
||||
}
|
||||
|
||||
if ( ! had_action )
|
||||
return null;
|
||||
|
||||
return (<div
|
||||
class={`ffz--hover-actions ffz-action-data tw-mg-r-05`}
|
||||
data-source="line"
|
||||
>
|
||||
{actions}
|
||||
</div>);
|
||||
}
|
||||
|
||||
|
||||
renderInline(msg, mod_icons, current_user, current_room, createElement, instance = null) {
|
||||
const actions = [];
|
||||
|
||||
|
@ -762,6 +901,8 @@ export default class Actions extends Module {
|
|||
if ( ! data.action || ! data.appearance )
|
||||
continue;
|
||||
|
||||
data.ctx = 'inline';
|
||||
|
||||
let ap = data.appearance || {};
|
||||
const disp = data.display || {},
|
||||
keys = disp.keys,
|
||||
|
@ -781,6 +922,12 @@ export default class Actions extends Module {
|
|||
if ( maybe_call(act.hidden, this, data, msg, current_room, current_user, mod_icons, instance) )
|
||||
continue;
|
||||
|
||||
if ( ap.type === 'dynamic' ) {
|
||||
const out = act.dynamicAppearance && act.dynamicAppearance.call(this, Object.assign({}, ap), data, msg, current_room, current_user, mod_icons, instance);
|
||||
if ( out )
|
||||
ap = out;
|
||||
}
|
||||
|
||||
if ( act.override_appearance ) {
|
||||
const out = act.override_appearance.call(this, Object.assign({}, ap), data, msg, current_room, current_user, mod_icons, instance);
|
||||
if ( out )
|
||||
|
@ -804,7 +951,7 @@ export default class Actions extends Module {
|
|||
had_action = true;
|
||||
list.push(<button
|
||||
class={`ffz-tooltip mod-icon ffz-mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}${keys ? ` ffz-modifier-${keys}` : ''}${hover ? ' ffz-hover' : ''}`}
|
||||
disabled={disabled}
|
||||
//disabled={disabled}
|
||||
data-tooltip-type="action"
|
||||
data-action={data.action}
|
||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||
|
@ -819,14 +966,6 @@ export default class Actions extends Module {
|
|||
if ( ! had_action )
|
||||
return null;
|
||||
|
||||
/*const room = current_room && JSON.stringify(current_room),
|
||||
user = msg.user && JSON.stringify({
|
||||
login: msg.user.login,
|
||||
displayName: msg.user.displayName,
|
||||
id: msg.user.id,
|
||||
type: msg.user.type
|
||||
});*/
|
||||
|
||||
let out = null;
|
||||
if ( actions.length )
|
||||
out = (<div
|
||||
|
|
|
@ -3,6 +3,22 @@
|
|||
import {load as loadFontAwesome} from 'utilities/font-awesome';
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Dynamic
|
||||
// ============================================================================
|
||||
|
||||
export const dynamic = {
|
||||
title: 'Dynamic',
|
||||
title_i18n: 'setting.actions.appearance.dynamic',
|
||||
|
||||
colored: true,
|
||||
|
||||
render(data, createElement, color) {
|
||||
return <figure style={{color}} class={`${data.icon||'ffz-i-zreknarf'}`} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Text
|
||||
// ============================================================================
|
||||
|
|
|
@ -18,14 +18,26 @@ export const pin = {
|
|||
required_context: ['message'],
|
||||
|
||||
title: 'Pin This Message',
|
||||
description: 'Allows you to pin a chat message. Only functions in channels that have access to Twitch\'s pinned messages experiment.',
|
||||
description: "Allows you to pin a chat message if you're a moderator.",
|
||||
|
||||
can_self: true,
|
||||
|
||||
tooltip() {
|
||||
tooltip(data) {
|
||||
const pinned = data.line?.props?.pinnedMessage?.message?.id === data.message_id;
|
||||
if (pinned)
|
||||
return this.i18n.t('chat.actions.pin.already', 'This message is already pinned.');
|
||||
|
||||
return this.i18n.t('chat.actions.pin', 'Pin This Message')
|
||||
},
|
||||
|
||||
disabled(data, message, current_room, current_user, mod_icons, instance) {
|
||||
const line = instance ?? data.line,
|
||||
props = line?.props,
|
||||
pinned = props?.pinnedMessage?.message?.id === message.id && message.id != null;
|
||||
|
||||
return pinned;
|
||||
},
|
||||
|
||||
hidden(data, message, current_room, current_user, mod_icons, instance) {
|
||||
let line = instance;
|
||||
|
||||
|
@ -61,19 +73,37 @@ export const pin = {
|
|||
export const reply = {
|
||||
presets: [{
|
||||
appearance: {
|
||||
type: 'icon',
|
||||
icon: 'ffz-i-reply'
|
||||
type: 'dynamic'
|
||||
}
|
||||
}],
|
||||
|
||||
required_context: ['message'],
|
||||
supports_dynamic: true,
|
||||
|
||||
title: 'Reply to Message',
|
||||
description: 'Allows you to directly reply to another user\'s message. Only functions when the Chat Replies Style is "FrankerFaceZ".',
|
||||
description: "Allows you to directly reply to another user's message.",
|
||||
|
||||
can_self: true,
|
||||
|
||||
tooltip() {
|
||||
dynamicAppearance(ap, data, message, current_room, current_user, mod_icons, instance) {
|
||||
const line = instance ?? data.line,
|
||||
props = line?.props,
|
||||
has_reply = props?.hasReply || props?.reply;
|
||||
|
||||
return {
|
||||
type: 'icon',
|
||||
icon: has_reply ? 'ffz-i-threads' : 'ffz-i-reply',
|
||||
color: ap.color
|
||||
}
|
||||
},
|
||||
|
||||
tooltip(data) {
|
||||
const props = data.line?.props,
|
||||
has_reply = props?.hasReply || props?.reply;
|
||||
|
||||
if (has_reply)
|
||||
return this.i18n.t('chat.actions.reply.thread', 'Open Thread');
|
||||
|
||||
return this.i18n.t('chat.actions.reply', 'Reply to Message')
|
||||
},
|
||||
|
||||
|
@ -82,10 +112,16 @@ export const reply = {
|
|||
if ( typeof id !== 'string' || ! /^[0-9a-f]+-[0-9a-f]+/.test(id) )
|
||||
return true;
|
||||
|
||||
if ( ! message.message || message.deleted || (current_user && current_user.login === message.user?.login) || ! current_user?.can_reply )
|
||||
// Users must be able to reply.
|
||||
if ( ! current_user?.can_reply )
|
||||
return true;
|
||||
|
||||
if ( message?.reply )
|
||||
// If reply mode is set to 0 (Disabled), don't show the action.
|
||||
if ( current_user?.reply_mode === 0 )
|
||||
return true;
|
||||
|
||||
// If the message is empty or deleted, don't show the action.
|
||||
if ( ! message.message || message.deleted )
|
||||
return true;
|
||||
},
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
>
|
||||
<option
|
||||
v-for="(r, key) in data.renderers"
|
||||
v-if="supportsRenderer(key)"
|
||||
:key="key"
|
||||
:value="key"
|
||||
>
|
||||
|
@ -330,7 +331,7 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="tw-pd-r-1 ffz-checkbox">
|
||||
<div v-if="has_hover_modifier" class="tw-pd-r-1 ffz-checkbox">
|
||||
<input
|
||||
:id="'key_hover$' + id"
|
||||
ref="key_hover"
|
||||
|
@ -388,8 +389,8 @@
|
|||
|
||||
<div v-if="canPreview" class="tw-mg-l-1 tw-border-l tw-pd-l-1 tw-pd-y-05 tw-flex tw-flex-shrink-0 tw-align-items-start">
|
||||
<action-preview
|
||||
:act="display"
|
||||
:color="display.appearance && data.color(display.appearance.color)"
|
||||
:act="maybeDynamic(display)"
|
||||
:process-color="data.color"
|
||||
:renderers="data.renderers"
|
||||
/>
|
||||
</div>
|
||||
|
@ -458,7 +459,7 @@ import {has, maybe_call, deep_copy} from 'utilities/object';
|
|||
let id = 0;
|
||||
|
||||
export default {
|
||||
props: ['action', 'data', 'inline', 'mod_icons', 'context', 'modifiers'],
|
||||
props: ['vuectx', 'action', 'data', 'inline', 'mod_icons', 'context', 'modifiers', 'hover_modifier'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
@ -494,6 +495,10 @@ export default {
|
|||
return this.modifiers
|
||||
},
|
||||
|
||||
has_hover_modifier() {
|
||||
return this.hover_modifier !== false
|
||||
},
|
||||
|
||||
fmts() {
|
||||
const out = [];
|
||||
|
||||
|
@ -691,7 +696,7 @@ export default {
|
|||
}));
|
||||
}
|
||||
|
||||
if ( disp.hover )
|
||||
if ( this.has_hover_modifier && disp.hover )
|
||||
out.push(this.t('setting.actions.visible.hover', 'when hovering'));
|
||||
|
||||
if ( ! out.length )
|
||||
|
@ -766,6 +771,29 @@ export default {
|
|||
this.edit_data = null;
|
||||
},
|
||||
|
||||
maybeDynamic(data) {
|
||||
let ap = data.appearance;
|
||||
if (ap?.type === 'dynamic') {
|
||||
const act = this.action_def,
|
||||
ffz = this.vuectx.getFFZ(),
|
||||
actions = ffz && ffz.resolve('chat.actions');
|
||||
|
||||
const out = actions && act?.dynamicAppearance && act.dynamicAppearance
|
||||
.call(actions, deep_copy(ap), data, null, null, null, null);
|
||||
if ( out )
|
||||
return Object.assign({}, data, {appearance: out});
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
supportsRenderer(key) {
|
||||
if (key !== 'dynamic')
|
||||
return true;
|
||||
|
||||
return this.action_def?.supports_dynamic;
|
||||
},
|
||||
|
||||
getData() {
|
||||
const def = this.display && this.data.actions[this.display.action];
|
||||
if ( ! def )
|
||||
|
|
|
@ -19,9 +19,13 @@
|
|||
<script>
|
||||
|
||||
export default {
|
||||
props: ['act', 'color', 'tooltip', 'pad', 'renderers'],
|
||||
props: ['act', 'process-color', 'tooltip', 'pad', 'renderers'],
|
||||
|
||||
computed: {
|
||||
color() {
|
||||
return this['processColor'](this.act.appearance.color);
|
||||
},
|
||||
|
||||
renderer() {
|
||||
return this.renderers[this.act.appearance.type]
|
||||
}
|
||||
|
|
|
@ -207,8 +207,8 @@
|
|||
<action-preview
|
||||
v-else
|
||||
:key="act.id"
|
||||
:act="act.v"
|
||||
:color="color(act.v.appearance.color)"
|
||||
:act="maybeDynamic(act.v)"
|
||||
:process-color="color"
|
||||
:renderers="data.renderers"
|
||||
tooltip="true"
|
||||
pad="true"
|
||||
|
@ -291,7 +291,12 @@
|
|||
<div class="tw-flex-grow-1 tw-mg-r-1">
|
||||
{{ preset.title_i18n ? t(preset.title_i18n, preset.title, preset) : preset.title }}
|
||||
</div>
|
||||
<action-preview v-if="preset.appearance" :act="preset" :renderers="data.renderers" />
|
||||
<action-preview
|
||||
v-if="preset.appearance"
|
||||
:act="maybeDynamic(preset)"
|
||||
:process-color="color"
|
||||
:renderers="data.renderers"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
@ -357,7 +362,9 @@
|
|||
:inline="item.inline"
|
||||
:mod_icons="has_icons"
|
||||
:context="item.context"
|
||||
:vuectx="context"
|
||||
:modifiers="item.modifiers"
|
||||
:hover_modifier="item.hover_modifier"
|
||||
@remove="remove(act)"
|
||||
@save="save(act, $event)"
|
||||
/>
|
||||
|
@ -739,6 +746,22 @@ export default {
|
|||
return true;
|
||||
},
|
||||
|
||||
maybeDynamic(data) {
|
||||
let ap = data.appearance;
|
||||
if (ap?.type === 'dynamic') {
|
||||
const act = this.data.actions[data.action],
|
||||
ffz = this.context.getFFZ(),
|
||||
actions = ffz && ffz.resolve('chat.actions');
|
||||
|
||||
const out = actions && act?.dynamicAppearance && act.dynamicAppearance
|
||||
.call(actions, deep_copy(ap), data, this.sample_message, this.sample_room, this.sample_user, this.with_mod_icons);
|
||||
if ( out )
|
||||
return Object.assign({}, data, {appearance: out});
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
color(input) {
|
||||
if ( ! input )
|
||||
return input;
|
||||
|
|
|
@ -370,6 +370,11 @@ export default {
|
|||
this.settings.off(':changed:debug.link-resolver.source', this.changeProvider, this);
|
||||
this.chat = null;
|
||||
this.settings = null;
|
||||
|
||||
if (this.es) {
|
||||
this.es.close();
|
||||
this.es = null;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
@focusin="focus"
|
||||
@focusout="blur"
|
||||
>
|
||||
<div class="scrollable-area tw-border-b" data-simplebar>
|
||||
<div ref="scroller" class="scrollable-area tw-border-b" data-simplebar>
|
||||
<div class="simplebar-scroll-content">
|
||||
<div ref="popup" class="simplebar-content">
|
||||
<div
|
||||
|
@ -111,7 +111,22 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
opened() {
|
||||
if (this.opened)
|
||||
this.$nextTick(() => this.updateScroller());
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateScroller() {
|
||||
const scroller = this.$refs.scroller;
|
||||
if (!scroller || ! window.ffzSimplebar || scroller.SimpleBar)
|
||||
return;
|
||||
|
||||
new ffzSimplebar(scroller, ffzSimplebar.getElOptions(scroller));
|
||||
},
|
||||
|
||||
openConfigure() {
|
||||
this.hide();
|
||||
this.$emit('navigate', 'data_management.profiles');
|
||||
|
|
4
src/sites/player/css_tweaks/player-fade-paused.scss
Normal file
4
src/sites/player/css_tweaks/player-fade-paused.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
.video-player__container[data-paused="true"] video,
|
||||
.video-player__container[data-buffering="true"] video {
|
||||
filter: grayscale(0.25) brightness(0.5);
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
.video-player__overlay[data-controls="false"][data-paused="false"][data-ended="false"] {
|
||||
.video-player__container[data-controls="false"][data-paused="false"][data-ended="false"] {
|
||||
cursor: none;
|
||||
}
|
|
@ -94,6 +94,17 @@ export default class PlayerBase extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.settings.add('player.fade-pause-buffer', {
|
||||
default: false,
|
||||
ui: {
|
||||
path: 'Player > General >> Playback',
|
||||
title: 'Fade the player when paused or buffering to make the UI easier to see.',
|
||||
component: 'setting-check-box'
|
||||
},
|
||||
|
||||
changed: val => this.css_tweaks.toggle('player-fade-paused', val)
|
||||
});
|
||||
|
||||
if ( HAS_COMPRESSOR ) {
|
||||
this.settings.add('player.compressor.enable', {
|
||||
default: true,
|
||||
|
@ -578,6 +589,7 @@ export default class PlayerBase extends Module {
|
|||
this.css_tweaks.toggle('player-volume', this.settings.get('player.volume-always-shown'));
|
||||
this.css_tweaks.toggle('player-ext-mouse', !this.settings.get('player.ext-interaction'));
|
||||
this.css_tweaks.toggle('player-hide-mouse', this.settings.get('player.hide-mouse'));
|
||||
this.css_tweaks.toggle('player-fade-paused', this.settings.get('player.fade-pause-buffer'));
|
||||
|
||||
this.installVisibilityHook();
|
||||
this.updateHideExtensions();
|
||||
|
@ -706,6 +718,7 @@ export default class PlayerBase extends Module {
|
|||
|
||||
const events = this.props.playerEvents;
|
||||
if ( events ) {
|
||||
on(events, 'Buffering', this._ffzUpdateState);
|
||||
on(events, 'Playing', this._ffzUpdateState);
|
||||
on(events, 'PlayerError', this._ffzUpdateState);
|
||||
on(events, 'PlayerError', this._ffzErrorReset);
|
||||
|
@ -818,6 +831,7 @@ export default class PlayerBase extends Module {
|
|||
|
||||
ds.ended = state === 'Ended';
|
||||
ds.paused = state === 'Idle';
|
||||
ds.buffering = state === 'Buffering';
|
||||
}
|
||||
|
||||
cls.prototype.ffzAttachListeners = function() {
|
||||
|
|
|
@ -67,6 +67,11 @@ export default class Twilight extends BaseSite {
|
|||
onEnable() {
|
||||
this.settings = this.resolve('settings');
|
||||
|
||||
this.web_munch.findModule('simplebar').then(sb => {
|
||||
if (! window.ffzSimplebar && sb )
|
||||
window.ffzSimplebar = sb;
|
||||
}).catch(() => {});
|
||||
|
||||
const thing = this.fine.searchNode(null, n => n?.pendingProps?.store?.getState),
|
||||
store = this.store = thing?.pendingProps?.store;
|
||||
|
||||
|
|
|
@ -769,24 +769,24 @@ export default class EmoteMenu extends Module {
|
|||
if ( renews > 0 ) {
|
||||
calendar = {
|
||||
icon: 'calendar',
|
||||
message: t.i18n.t('emote-menu.sub-renews', 'This sub renews in {seconds,humantime}.', {seconds: renews})
|
||||
message: t.i18n.t('emote-menu.sub-renews', 'This sub renews {seconds,humantime}.', {seconds: renews})
|
||||
}
|
||||
|
||||
} else if ( ends ) {
|
||||
if ( data.prime )
|
||||
calendar = {
|
||||
icon: 'crown',
|
||||
message: t.i18n.t('emote-menu.sub-prime', 'This is your free sub with Prime Gaming.\nIt ends in {seconds,humantime}.', {seconds: ends})
|
||||
message: t.i18n.t('emote-menu.sub-prime', 'This is your free sub with Prime Gaming.\nIt ends {seconds,humantime}.', {seconds: ends})
|
||||
}
|
||||
else if ( data.gift )
|
||||
calendar = {
|
||||
icon: 'gift',
|
||||
message: t.i18n.t('emote-menu.sub-gift-ends', 'This gifted sub ends in {seconds,humantime}.', {seconds: ends})
|
||||
message: t.i18n.t('emote-menu.sub-gift-ends', 'This gifted sub ends {seconds,humantime}.', {seconds: ends})
|
||||
}
|
||||
else
|
||||
calendar = {
|
||||
icon: 'calendar-empty',
|
||||
message: t.i18n.t('emote-menu.sub-ends', 'This sub ends in {seconds,humantime}.', {seconds: ends})
|
||||
message: t.i18n.t('emote-menu.sub-ends', 'This sub ends {seconds,humantime}.', {seconds: ends})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -652,7 +652,25 @@ export default class ChatHook extends Module {
|
|||
default: true,
|
||||
ui: {
|
||||
path: 'Chat > Input >> Appearance',
|
||||
title: 'Display the Mod View button in relevant channels.',
|
||||
title: 'Allow the "Mod View" button to appear in relevant channels.',
|
||||
component: 'setting-check-box'
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.input.show-highlight', {
|
||||
default: true,
|
||||
ui: {
|
||||
path: 'Chat > Input >> Appearance',
|
||||
title: 'Allow the "Chat Highlight Settings" button to appear in relevant channels.',
|
||||
component: 'setting-check-box'
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.input.show-shield', {
|
||||
default: true,
|
||||
ui: {
|
||||
path: 'Chat > Input >> Appearance',
|
||||
title: 'Allow the "Shield Mode" button to appear in relevant channels.',
|
||||
component: 'setting-check-box'
|
||||
}
|
||||
});
|
||||
|
@ -920,7 +938,13 @@ export default class ChatHook extends Module {
|
|||
this.css_tweaks.toggleHide('last-x-events', ! val));
|
||||
|
||||
this.chat.context.getChanges('chat.input.show-mod-view', val =>
|
||||
this.css_tweaks.toggleHide('mod-view', ! val));
|
||||
this.css_tweaks.toggleHide('ci-mod-view', ! val));
|
||||
|
||||
this.chat.context.getChanges('chat.input.show-highlight', val =>
|
||||
this.css_tweaks.toggleHide('ci-highlight-settings', !val));
|
||||
|
||||
this.chat.context.getChanges('chat.input.show-shield', val =>
|
||||
this.css_tweaks.toggleHide('ci-shield-mode', ! val));
|
||||
|
||||
this.chat.context.getChanges('chat.lines.padding', val =>
|
||||
this.css_tweaks.toggle('chat-padding', val));
|
||||
|
|
|
@ -352,7 +352,8 @@ export default class ChatLine extends Module {
|
|||
props.message !== this.props.message ||
|
||||
props.isCurrentUserModerator !== this.props.isCurrentUserModerator ||
|
||||
props.showModerationIcons !== this.props.showModerationIcons ||
|
||||
props.showTimestamps !== this.props.showTimestamps;
|
||||
props.showTimestamps !== this.props.showTimestamps ||
|
||||
(props.pinnedMessage?.message?.id === props.message?.id) !== (this.props.pinnedMessage?.message?.id === this.props.message?.id);
|
||||
}
|
||||
|
||||
cls.prototype.ffzOpenReply = function() {
|
||||
|
@ -361,6 +362,22 @@ export default class ChatLine extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( this.props.hasReply ) {
|
||||
const msg = this.props.message;
|
||||
if (msg?.user) {
|
||||
this.setOPCardTray({
|
||||
parentMsgId: msg.id,
|
||||
parentUid: msg.user.userID,
|
||||
parentUserLogin: msg.user.userLogin,
|
||||
parentDeleted: msg.deleted,
|
||||
parentDisplayName: msg.user.userDisplayName,
|
||||
parentMessageBody: msg.messageBody
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const old_render_author = this.renderMessageAuthor;
|
||||
this.renderMessageAuthor = () => this.ffzReplyAuthor();
|
||||
|
||||
|
@ -487,7 +504,8 @@ export default class ChatLine extends Module {
|
|||
if ( u ) {
|
||||
u.moderator = this.props.isCurrentUserModerator;
|
||||
u.staff = this.props.isCurrentUserStaff;
|
||||
u.can_reply = reply_mode === 2 && can_reply;
|
||||
u.reply_mode = reply_mode;
|
||||
u.can_reply = can_reply;
|
||||
}
|
||||
|
||||
if ( ! this.ffz_open_reply )
|
||||
|
@ -663,23 +681,23 @@ other {# messages were deleted by a moderator.}
|
|||
room_id = msg.roomId = r.id;
|
||||
}
|
||||
|
||||
//if ( ! msg.message && msg.messageParts )
|
||||
// t.chat.detokenizeMessage(msg);
|
||||
|
||||
const u = t.site.getUser(),
|
||||
r = {id: room_id, login: room};
|
||||
|
||||
const has_replies = this.props && !!(this.props.hasReply || this.props.reply || ! this.props.replyRestrictedReason),
|
||||
can_replies = has_replies && msg.message && ! msg.deleted && ! this.props.disableReplyClick,
|
||||
can_reply = can_replies && u && u.login !== msg.user?.login && ! msg.reply,
|
||||
twitch_clickable = reply_mode === 1 && can_replies && (!!msg.reply || can_reply);
|
||||
can_reply = can_replies && (has_replies || (u && u.login !== msg.user?.login));
|
||||
|
||||
if ( u ) {
|
||||
u.moderator = this.props.isCurrentUserModerator;
|
||||
u.staff = this.props.isCurrentUserStaff;
|
||||
u.can_reply = reply_mode === 2 && can_reply;
|
||||
u.reply_mode = reply_mode;
|
||||
u.can_reply = can_reply;
|
||||
}
|
||||
|
||||
const hover_actions = t.actions.renderHover(msg, this.props.showModerationIcons, u, r, e, this),
|
||||
twitch_clickable = hover_actions != null;
|
||||
|
||||
const tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u),
|
||||
rich_content = FFZRichContent && t.chat.pluckRichContent(tokens, msg),
|
||||
bg_css = msg.mentioned && msg.mention_color ? t.parent.inverse_colors.process(msg.mention_color) : null;
|
||||
|
@ -762,8 +780,6 @@ other {# messages were deleted by a moderator.}
|
|||
(this.props.showTimestamps || this.props.isHistorical) && e('span', {
|
||||
className: 'chat-line__timestamp'
|
||||
}, t.chat.formatTime(msg.timestamp)),
|
||||
//twitch_clickable ?
|
||||
// e('div', {className: 'chat-line__username-container tw-inline-block'}, user_bits) :
|
||||
user_bits,
|
||||
e('span', {'aria-hidden': true}, is_action ? ' ' : ': '),
|
||||
show && has_replies && reply_tokens ?
|
||||
|
@ -785,19 +801,6 @@ other {# messages were deleted by a moderator.}
|
|||
show && rich_content && e(FFZRichContent, rich_content),
|
||||
|
||||
mod_action,
|
||||
|
||||
/*this.state.renderDebug === 2 && e('div', {
|
||||
className: 'border mg-t-05'
|
||||
}, old_render.call(this)),
|
||||
|
||||
this.state.renderDebug === 1 && e('div', {
|
||||
className: 'message--debug',
|
||||
style: {
|
||||
fontFamily: 'monospace',
|
||||
whiteSpace: 'pre-wrap',
|
||||
lineHeight: '1.1em'
|
||||
}
|
||||
}, JSON.stringify([tokens, msg.emotes], null, 2))*/
|
||||
] : null;
|
||||
|
||||
if ( out == null )
|
||||
|
@ -1136,37 +1139,11 @@ other {# messages were deleted by a moderator.}
|
|||
}),
|
||||
e('div', {
|
||||
className: 'chat-line__message-container tw-relative'
|
||||
}, [
|
||||
}, reply_mode == 1 ? [
|
||||
this.props.repliesAppearancePreference && this.props.repliesAppearancePreference === 'expanded' ? this.renderReplyLine() : null,
|
||||
out
|
||||
]),
|
||||
/*e('div', {
|
||||
className: 'chat-line__icons'
|
||||
}, [*/
|
||||
/*e('div', {
|
||||
className: 'chat-line__pin-icon tw-absolute tw-border-radius-medium tw-c-background-base tw-elevation-1'
|
||||
}, e('button', {
|
||||
className: 'tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-button-icon ffz-core-button tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative ffz-tooltip ffz-tooltip--no-mouse',
|
||||
'data-test-selector': 'chat-pin-button',
|
||||
'aria-label': 'Pin This Message',
|
||||
'data-title': 'Pin This Message',
|
||||
onClick: this.onPinMessageClick
|
||||
}, e('span', {
|
||||
className: 'tw-button-icon__icon'
|
||||
}, e('figure', {className: 'ffz-i-'})))),
|
||||
//this.renderPinButton(),*/
|
||||
e('div', {
|
||||
className: 'chat-line__reply-icon tw-absolute tw-border-radius-medium tw-c-background-base tw-elevation-1'
|
||||
}, e('button', {
|
||||
className: 'tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-button-icon ffz-core-button tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative ffz-tooltip ffz-tooltip--no-mouse',
|
||||
'data-test-selector': 'chat-reply-button',
|
||||
'aria-label': title,
|
||||
'data-title': title,
|
||||
onClick: this.ffz_open_reply
|
||||
}, e('span', {
|
||||
className: 'tw-button-icon__icon'
|
||||
}, icon)))
|
||||
//])
|
||||
] : out),
|
||||
hover_actions
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -108,6 +108,11 @@ export default class Scroller extends Module {
|
|||
this.use_keys = true;
|
||||
break;
|
||||
}
|
||||
for(const act of this.chat.context.get('chat.actions.hover'))
|
||||
if ( act && act.display && act.display.keys ) {
|
||||
this.use_keys = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( this.use_keys !== old_use ) {
|
||||
for(const inst of this.ChatScroller.instances)
|
||||
|
|
|
@ -39,7 +39,7 @@ const CLASSES = {
|
|||
'player-event-bar': '.channel-root .live-event-banner-ui__header',
|
||||
'player-rerun-bar': '.channel-root__player-container div.tw-c-text-overlay:not([data-a-target="hosting-ui-header"])',
|
||||
|
||||
'pinned-cheer': '.pinned-cheer,.pinned-cheer-v2,.channel-leaderboard,.channel-leaderboard-marquee',
|
||||
'pinned-cheer': '.pinned-cheer,.pinned-cheer-v2,.channel-leaderboard,.channel-leaderboard-marquee,div[data-test-selector="channel-leaderboard-container"]',
|
||||
'whispers': 'body .whispers-open-threads,.tw-core-button[data-a-target="whisper-box-button"],.whispers__pill',
|
||||
|
||||
'dir-live-ind': '.live-channel-card[data-ffz-type="live"] .tw-channel-status-text-indicator, article[data-ffz-type="live"] .tw-channel-status-text-indicator',
|
||||
|
@ -47,8 +47,12 @@ const CLASSES = {
|
|||
'not-live-bar': 'div[data-test-selector="non-live-video-banner-layout"]',
|
||||
'channel-live-ind': '.channel-header__user .tw-channel-status-text-indicator,.channel-info-content .tw-halo__indicator',
|
||||
'celebration': 'body .celebration__overlay',
|
||||
'mod-view': '.chat-input__buttons-container a[href*="/moderator"]',
|
||||
'last-x-events': '.last-x-events_container'
|
||||
|
||||
'last-x-events': '.last-x-events_container',
|
||||
|
||||
'ci-mod-view': '.chat-input__buttons-container a[href*="/moderator"]',
|
||||
'ci-highlight-settings': '.chat-input__buttons-container button[data-highlight-selector="chat-highlights-shortcut"]',
|
||||
'ci-shield-mode': '.chat-input__buttons-container > div:last-child button[class|="ScCoreButton"]:not([data-highlight-selector]):not([data-a-target])'
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
.video-player__container[data-paused="true"] video,
|
||||
.video-player__container[data-buffering="true"] video {
|
||||
filter: grayscale(0.25) brightness(0.5);
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
.video-player__overlay[data-controls="false"][data-paused="false"][data-ended="false"] {
|
||||
.video-player__container[data-controls="false"][data-paused="false"][data-ended="false"] {
|
||||
cursor: none;
|
||||
}
|
|
@ -271,6 +271,7 @@ export default class ThemeEngine extends Module {
|
|||
`--color-background-input-focus:${color.toCSS()};`,
|
||||
`--color-background-base:${hsla._l(luma + (dark ? 0.05 : -0.05)).toCSS()};`,
|
||||
`--color-background-alt:${hsla._l(luma + (dark ? 0.1 : -0.1)).toCSS()};`,
|
||||
`--color-background-float:var(--color-background-alt);`,
|
||||
`--color-background-alt-2:${hsla._l(luma + (dark ? 0.15 : -0.15)).toCSS()};`
|
||||
].join('');
|
||||
}
|
||||
|
|
|
@ -452,4 +452,44 @@
|
|||
padding: 0.5rem !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ffz--hover-actions {
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: -1rem;
|
||||
display: none;
|
||||
|
||||
--ffz-chat-actions-size: 2rem;
|
||||
|
||||
.chat-line__message:hover & {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ffz-hover-action {
|
||||
display: inline-block;
|
||||
margin-left: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
box-shadow: var(--shadow-elevation-1);
|
||||
background-color: var(--color-background-base);
|
||||
border-radius: var(--border-radius-medium);
|
||||
|
||||
&.ffz-has-modifier {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > .ffz-mod-icon {
|
||||
padding: 0.5rem;
|
||||
|
||||
background-color: var(--color-background-button-text-default);
|
||||
color: var(--color-fill-button-icon);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-background-button-text-hover);
|
||||
color: var(--color-fill-button-icon-hover);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,14 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const scroller = this.$refs.scroller;
|
||||
if (!scroller || ! window.ffzSimplebar || scroller.SimpleBar)
|
||||
return;
|
||||
|
||||
new ffzSimplebar(scroller, ffzSimplebar.getElOptions(scroller));
|
||||
},
|
||||
|
||||
methods: {
|
||||
onScroll() {
|
||||
// We do this to avoid the scroll position getting screwed up on
|
||||
|
|
|
@ -40,6 +40,7 @@ export const RERENDER_SETTINGS = [
|
|||
'chat.subs.show',
|
||||
'chat.subs.compact',
|
||||
'chat.actions.inline',
|
||||
'chat.actions.hover',
|
||||
'chat.timestamp-format',
|
||||
'chat.points.allow-highlight',
|
||||
'chat.filtering.display-deleted',
|
||||
|
|
|
@ -104,5 +104,7 @@ export default [
|
|||
"reply",
|
||||
"threads",
|
||||
"right-open",
|
||||
"list-bullet"
|
||||
"list-bullet",
|
||||
"mastodon",
|
||||
"volume-up"
|
||||
];
|
|
@ -802,8 +802,13 @@ function render_image(token, createElement, ctx) {
|
|||
const image = createElement('img', {
|
||||
className: `${token.class || ''} ${round}`,
|
||||
src: token.url,
|
||||
alt: token.alt || token.title || '',
|
||||
title: token.title || '',
|
||||
onLoad: ctx.onload
|
||||
onLoad: ctx.onload,
|
||||
style: {
|
||||
width: token.width,
|
||||
height: token.height
|
||||
}
|
||||
});
|
||||
|
||||
if ( ! aspect )
|
||||
|
|
|
@ -373,7 +373,12 @@
|
|||
&[data-meta="true"][data-alt="true"][data-ctrl="true"] .ffz-modifier-13,
|
||||
&[data-meta="true"][data-alt="true"][data-shift="true"] .ffz-modifier-14,
|
||||
&[data-meta="true"][data-alt="true"][data-ctrl="true"][data-shift="true"] .ffz-modifier-15 {
|
||||
|
||||
display: inline-flex;
|
||||
|
||||
&:ffz-hover-action {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,8 @@
|
|||
.ffz-i-threads:before { content: '\e844'; } /* '' */
|
||||
.ffz-i-volume-off:before { content: '\e845'; } /* '' */
|
||||
.ffz-i-right-open:before { content: '\e846'; } /* '' */
|
||||
.ffz-i-mastodon:before { content: '\e847'; } /* '' */
|
||||
.ffz-i-volume-up:before { content: '\e848'; } /* '' */
|
||||
.ffz-i-move:before { content: '\f047'; } /* '' */
|
||||
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -70,6 +70,8 @@
|
|||
.ffz-i-threads { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-volume-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-right-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-mastodon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-volume-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
|
|
@ -81,6 +81,8 @@
|
|||
.ffz-i-threads { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-volume-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-right-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-mastodon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-volume-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@font-face {
|
||||
font-family: 'ffz-fontello';
|
||||
src: url('../font/ffz-fontello.eot?79984461');
|
||||
src: url('../font/ffz-fontello.eot?79984461#iefix') format('embedded-opentype'),
|
||||
url('../font/ffz-fontello.woff2?79984461') format('woff2'),
|
||||
url('../font/ffz-fontello.woff?79984461') format('woff'),
|
||||
url('../font/ffz-fontello.ttf?79984461') format('truetype'),
|
||||
url('../font/ffz-fontello.svg?79984461#ffz-fontello') format('svg');
|
||||
src: url('../font/ffz-fontello.eot?19069837');
|
||||
src: url('../font/ffz-fontello.eot?19069837#iefix') format('embedded-opentype'),
|
||||
url('../font/ffz-fontello.woff2?19069837') format('woff2'),
|
||||
url('../font/ffz-fontello.woff?19069837') format('woff'),
|
||||
url('../font/ffz-fontello.ttf?19069837') format('truetype'),
|
||||
url('../font/ffz-fontello.svg?19069837#ffz-fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
|||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'ffz-fontello';
|
||||
src: url('../font/ffz-fontello.svg?79984461#ffz-fontello') format('svg');
|
||||
src: url('../font/ffz-fontello.svg?19069837#ffz-fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -125,6 +125,8 @@
|
|||
.ffz-i-threads:before { content: '\e844'; } /* '' */
|
||||
.ffz-i-volume-off:before { content: '\e845'; } /* '' */
|
||||
.ffz-i-right-open:before { content: '\e846'; } /* '' */
|
||||
.ffz-i-mastodon:before { content: '\e847'; } /* '' */
|
||||
.ffz-i-volume-up:before { content: '\e848'; } /* '' */
|
||||
.ffz-i-move:before { content: '\f047'; } /* '' */
|
||||
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
||||
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue