diff --git a/package.json b/package.json index 5e841d08..1e3277d5 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.31.6", + "version": "4.32.0", "description": "FrankerFaceZ is a Twitch enhancement suite.", "private": true, "license": "Apache-2.0", diff --git a/src/modules/chat/emoji.js b/src/modules/chat/emoji.js index 76546934..717749ab 100644 --- a/src/modules/chat/emoji.js +++ b/src/modules/chat/emoji.js @@ -47,7 +47,9 @@ export const SKIN_TONES = { 4: '1f3fe', 5: '1f3ff' }; + export const JOINER_REPLACEMENT = /(? Behavior >> Emoji', + title: 'Emoji Joiner Workaround', + description: 'This feature is intended to allow the use of combined emoji in supported clients. This is required due to a bug in TMI that strips ZWJ characters from chat messages. [Visit the original issue](https://github.com/FrankerFaceZ/FrankerFaceZ/issues/1147) for more details.', + component: 'setting-select-box', + data: [ + {value: 0, title: 'Disabled'}, + {value: 1, title: 'Display Only'}, + {value: 2, title: 'Display and Send'} + ] + } + }); + this.settings.add('chat.emoji.style', { default: 'twitter', process(ctx, val) { @@ -110,6 +127,13 @@ export default class Emoji extends Module { } onEnable() { + this.on('chat:pre-send-message', event => { + if (event.context.get('chat.emoji.replace-joiner') < 2) + return; + + event.message = event.message.replace(ZWD_REPLACEMENT, EMOJI_JOINER); + }); + this.loadEmojiData(); } diff --git a/src/modules/chat/emotes.js b/src/modules/chat/emotes.js index 3c81719b..dfc0d9ec 100644 --- a/src/modules/chat/emotes.js +++ b/src/modules/chat/emotes.js @@ -103,12 +103,22 @@ export default class Emotes extends Module { }); this.settings.add('chat.emotes.2x', { - default: false, + default: 0, + process(ctx, val) { + if ( val === true ) return 1; + else if ( val === false ) return 0; + return val; + }, ui: { path: 'Chat > Appearance >> Emotes', title: 'Larger Emotes', description: 'This setting will make emotes appear twice as large in chat. It\'s good for use with larger fonts or just if you really like emotes.', - component: 'setting-check-box' + component: 'setting-select-box', + data: [ + {value: 0, title: 'Disabled'}, + {value: 1, title: 'Emotes'}, + {value: 2, title: 'Emotes and Emoji'} + ] } }); diff --git a/src/modules/chat/tokenizers.jsx b/src/modules/chat/tokenizers.jsx index 9ccf7ede..8e43cdc2 100644 --- a/src/modules/chat/tokenizers.jsx +++ b/src/modules/chat/tokenizers.jsx @@ -1431,7 +1431,7 @@ export const AddonEmotes = { if ( ! emotes ) return; - const big = this.context.get('chat.emotes.2x'), + const big = this.context.get('chat.emotes.2x') > 0, anim = this.context.get('chat.emotes.animated'), out = []; @@ -1531,6 +1531,7 @@ export const Emoji = { return; const splitter = this.emoji.splitter, + replace = this.context.get('chat.emoji.replace-joiner') > 0, style = this.context.get('chat.emoji.style'); if ( style === 0 ) @@ -1547,7 +1548,9 @@ export const Emoji = { continue; } - const text = token.text.replace(JOINER_REPLACEMENT, "\u200d"); + const text = replace ? + token.text.replace(JOINER_REPLACEMENT, "\u200d") : + token.text; splitter.lastIndex = 0; let idx = 0, match; @@ -1610,7 +1613,7 @@ export const TwitchEmotes = { const data = msg.ffz_emotes, anim = this.context.get('chat.emotes.animated'), - big = this.context.get('chat.emotes.2x'), + big = this.context.get('chat.emotes.2x') > 0, use_replacements = this.context.get('chat.fix-bad-emotes'), emotes = []; diff --git a/src/sites/twitch-twilight/modules/chat/index.js b/src/sites/twitch-twilight/modules/chat/index.js index 2e4bc997..a1b86a49 100644 --- a/src/sites/twitch-twilight/modules/chat/index.js +++ b/src/sites/twitch-twilight/modules/chat/index.js @@ -139,7 +139,9 @@ const CHAT_TYPES = make_enum( 'ChannelPointsReward', 'CommunityChallengeContribution', 'CelebrationPurchase', - 'LiveMessageSeparator' + 'LiveMessageSeparator', + 'RestrictedLowTrustUserMessage', + 'CommunityIntroduction' ); @@ -292,6 +294,7 @@ export default class ChatHook extends Module { data: () => Object .keys(this.chat_types) .filter(key => ! UNBLOCKABLE_TYPES.includes(key) && ! /^\d+$/.test(key)) + .sort() } }); @@ -887,33 +890,21 @@ export default class ChatHook extends Module { this.CalloutSelector.forceUpdate(); }, this); + this.chat.context.getChanges('chat.emotes.2x', val => + this.css_tweaks.toggle('big-emoji', val > 1)); + this.chat.context.getChanges('chat.input.show-mod-view', val => this.css_tweaks.toggleHide('mod-view', ! val)); - /*this.chat.context.on('changed:chat.input.show-mod-view', val => this.css_tweaks.toggleHide('mod-view', ! val)); - this.css_tweaks.toggleHide('mod-view', ! this.chat.context.get('chat.input.show-mod-view'));*/ - this.chat.context.getChanges('chat.lines.padding', val => this.css_tweaks.toggle('chat-padding', val)); - /*this.chat.context.on('changed:chat.lines.padding', val => - this.css_tweaks.toggle('chat-padding', val)); - this.css_tweaks.toggle('chat-padding', this.chat.context.get('chat.lines.padding'));*/ - this.chat.context.getChanges('chat.bits.show', val => this.css_tweaks.toggle('hide-bits', !val)); - /*this.chat.context.on('changed:chat.bits.show', val => - this.css_tweaks.toggle('hide-bits', !val)); - this.css_tweaks.toggle('hide-bits', !this.chat.context.get('chat.bits.show'));*/ - this.chat.context.getChanges('chat.bits.show-pinned', val => this.css_tweaks.toggleHide('pinned-cheer', !val)); - /*this.chat.context.on('changed:chat.bits.show-pinned', val => - this.css_tweaks.toggleHide('pinned-cheer', !val)); - this.css_tweaks.toggleHide('pinned-cheer', !this.chat.context.get('chat.bits.show-pinned'));*/ - this.chat.context.getChanges('chat.filtering.deleted-style', val => { this.css_tweaks.toggle('chat-deleted-strike', val === 1 || val === 2); this.css_tweaks.toggle('chat-deleted-fade', val < 2); @@ -922,35 +913,17 @@ export default class ChatHook extends Module { this.chat.context.getChanges('chat.filtering.clickable-mentions', val => this.css_tweaks.toggle('clickable-mentions', val)); - /*this.chat.context.on('changed:chat.filtering.clickable-mentions', val => - this.css_tweaks.toggle('clickable-mentions', val)); - this.css_tweaks.toggle('clickable-mentions', this.chat.context.get('chat.filtering.clickable-mentions'));*/ - this.chat.context.getChanges('chat.filtering.bold-mentions', val => this.css_tweaks.toggle('chat-mention-no-bold', ! val)); - /*this.chat.context.on('changed:chat.filtering.bold-mentions', val => - this.css_tweaks.toggle('chat-mention-no-bold', ! val)); - this.css_tweaks.toggle('chat-mention-no-bold', ! this.chat.context.get('chat.filtering.bold-mentions'));*/ - this.chat.context.getChanges('chat.hide-community-highlights', val => this.css_tweaks.toggleHide('community-highlights', val)); - /*this.chat.context.on('changed:chat.hide-community-highlights', val => - this.css_tweaks.toggleHide('community-highlights', val)); - this.css_tweaks.toggleHide('community-highlights', this.chat.context.get('chat.hide-community-highlights'));*/ - this.chat.context.getChanges('chat.lines.alternate', val => { this.css_tweaks.toggle('chat-rows', val); this.updateMentionCSS(); }); - /*this.chat.context.on('changed:chat.lines.alternate', val => { - this.css_tweaks.toggle('chat-rows', val); - this.updateMentionCSS(); - }); - this.css_tweaks.toggle('chat-rows', this.chat.context.get('chat.lines.alternate'));*/ - this.updateChatCSS(); this.updateColors(); this.updateLineBorders(); @@ -1534,30 +1507,6 @@ export default class ChatHook extends Module { if ( msg.type === types.RewardGift && ! t.chat.context.get('chat.bits.show-rewards') ) return; - if ( msg.type === types.CommunityIntroduction ) { - // TODO: Make this better. - msg = { - type: types.Message, - badgeDynamicData: {}, - badges: {}, - id: msg.id, - isFirstMsg: true, - message: msg.message, - messageBody: msg.message, - messageParts: [ - {type: 0, content: msg.message} - ], - messageType: 0, - channel: msg.channel, - timestamp: new Date(), - user: { - userDisplayName: msg.displayName, - userLogin: msg.login, - userID: msg.userID - } - }; - } - if ( msg.type === types.Message ) { const m = t.chat.standardizeMessage(msg), cont = inst._ffz_connector ?? inst.ffzGetConnector(); @@ -2082,6 +2031,7 @@ export default class ChatHook extends Module { const event = new FFZEvent({ message: msg, extra, + context: t.chat.context, channel: inst.props.channelLogin, addMessage, sendMessage @@ -2281,14 +2231,21 @@ export default class ChatHook extends Module { } } - /*this.onCommunityIntroductionEvent = function(e) { + const old_communityintro = this.onCommunityIntroductionEvent; + this.onCommunityIntroductionEvent = function(e) { try { - + if ( t.chat.context.get('chat.filtering.blocked-types').has('CommunityIntroduction') ) { + const out = i.convertMessage(e); + return i.postMessageToCurrentChannel(e, out); + } } catch(err) { t.log.capture(err, {extra: e}); + t.log.error(err); } - }*/ + + return old_communityintro.call(this, e); + } const old_anonsubgift = this.onAnonSubscriptionGiftEvent; this.onAnonSubscriptionGiftEvent = function(e) { @@ -2487,7 +2444,7 @@ export default class ChatHook extends Module { // For certain message types, the message is contained within // a message sub-object. - if ( message.type === t.chat_types.ChannelPointsReward ) + if ( message.type === t.chat_types.ChannelPointsReward || message.type === t.chat_types.CommunityIntroduction || (message.message?.user & message.message?.badgeDynamicData) ) message = message.message; if ( original.channel ) { diff --git a/src/sites/twitch-twilight/modules/chat/input.jsx b/src/sites/twitch-twilight/modules/chat/input.jsx index 359256f4..5c57ee0e 100644 --- a/src/sites/twitch-twilight/modules/chat/input.jsx +++ b/src/sites/twitch-twilight/modules/chat/input.jsx @@ -12,8 +12,6 @@ import { TWITCH_POINTS_SETS, TWITCH_GLOBAL_SETS, TWITCH_PRIME_SETS, KNOWN_CODES, import Twilight from 'site'; -import {EMOJI_JOINER} from 'src/modules/chat/emoji'; - // Prefer using these statically-allocated collators to String.localeCompare const locale = Intl.Collator(); const localeCaseInsensitive = Intl.Collator(undefined, {sensitivity: 'accent'}); @@ -509,9 +507,6 @@ export default class Input extends Module { t.log.error(err); } - // replace ZERO WIDTH JOINER with custom joiner so combined emoji work - inst.autocompleteInputRef.setValue(inst.chatInputRef.value.replace('\u{200d}', EMOJI_JOINER)); - originalOnMessageSend.call(this, event); } } diff --git a/src/sites/twitch-twilight/modules/chat/line.js b/src/sites/twitch-twilight/modules/chat/line.js index 4db3ec83..489502c5 100644 --- a/src/sites/twitch-twilight/modules/chat/line.js +++ b/src/sites/twitch-twilight/modules/chat/line.js @@ -37,6 +37,114 @@ export default class ChatLine extends Module { this.inject('chat.actions'); this.inject('chat.overrides'); + /*this.line_types = {}; + + this.line_types.sub_mystery = (msg, u, r, inst, e) => { + const mystery = msg.mystery; + if ( mystery ) + mystery.line = this; + + const sub_msg = this.i18n.tList('chat.sub.gift', "{user} is gifting {count, plural, one {# Tier {tier} Sub} other {# Tier {tier} Subs}} to {channel}'s community! ", { + user: (msg.sub_anon || msg.user.username === 'ananonymousgifter') ? + this.i18n.t('chat.sub.anonymous-gifter', 'An anonymous gifter') : + e('span', { + role: 'button', + className: 'chatter-name', + onClick: inst.ffz_user_click_handler + }, e('span', { + className: 'tw-c-text-base tw-strong' + }, msg.user.displayName)), + count: msg.sub_count, + tier: SUB_TIERS[msg.sub_plan] || 1, + channel: msg.roomLogin + }); + + if ( msg.sub_total === 1 ) + sub_msg.push(this.i18n.t('chat.sub.gift-first', "It's their first time gifting a Sub in the channel!")); + else if ( msg.sub_total > 1 ) + sub_msg.push(this.i18n.t('chat.sub.gift-total', "They've gifted {count} Subs in the channel!", { + count: msg.sub_total + })); + + if ( ! inst.ffz_click_expand ) + inst.ffz_click_expand = () => { + inst.setState({ + ffz_expanded: ! inst.state.ffz_expanded + }); + } + + const expanded = this.chat.context.get('chat.subs.merge-gifts-visibility') ? + ! inst.state.ffz_expanded : inst.state.ffz_expanded; + + let sub_list = null; + if( expanded && mystery && mystery.recipients && mystery.recipients.length > 0 ) { + const the_list = []; + for(const x of mystery.recipients) { + if ( the_list.length ) + the_list.push(', '); + + the_list.push(e('span', { + role: 'button', + className: 'ffz--giftee-name', + onClick: inst.ffz_user_click_handler, + 'data-user': JSON.stringify(x) + }, e('span', { + className: 'tw-c-text-base tw-strong' + }, x.displayName))); + } + + sub_list = e('div', { + className: 'tw-mg-t-05 tw-border-t tw-pd-t-05 tw-c-text-alt-2' + }, the_list); + } + + const extra_ts = this.chat.context.get('chat.extra-timestamps'); + + return inst.ffzDrawLine( + msg, + `ffz-notice-line user-notice-line tw-pd-y-05 ffz--subscribe-line`, + [ + e('div', { + className: 'tw-flex tw-c-text-alt-2', + onClick: inst.ffz_click_expand + }, [ + this.chat.context.get('chat.subs.compact') ? null : + e('figure', { + className: `ffz-i-star${msg.sub_anon ? '-empty' : ''} tw-mg-r-05` + }), + e('div', null, [ + extra_ts && (inst.props.showTimestamps || inst.props.isHistorical) && e('span', { + className: 'chat-line__timestamp' + }, this.chat.formatTime(msg.timestamp)), + msg.sub_anon ? null : this.actions.renderInline(msg, inst.props.showModerationIcons, u, r, e), + sub_msg + ]), + mystery ? e('div', { + className: 'tw-pd-l-05 tw-font-size-4' + }, e('figure', { + className: `ffz-i-${expanded ? 'down' : 'right'}-dir tw-pd-y-1` + })) : null + ]), + sub_list + ], + [ + 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-title': 'Test' + }, e('span', { + className: 'tw-button-icon__icon' + }, e('figure', {className: 'ffz-i-threads'}))), + 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-title': 'Thing' + }, e('span', { + className: 'tw-button-icon__icon' + }, e('figure', {className: 'ffz-i-cog'}))) + ], + null + ); + }*/ + this.ChatLine = this.fine.define( 'chat-line', n => n.renderMessageBody && n.props && ! n.onExtensionNameClick && !has(n.props, 'hasModPermissions'), @@ -316,6 +424,139 @@ export default class ChatLine extends Module { ]); } + cls.prototype.ffzDrawLine = function(msg, cls, out, hover_actions, bg_css) { + const anim_hover = t.chat.context.get('chat.emotes.animated') === 2; + + if (hover_actions) { + cls = `${cls} tw-relative`; + out = [ + e('div', { + className: 'chat-line__message-highlight tw-absolute tw-border-radius-medium tw-top-0 tw-bottom-0 tw-right-0 tw-left-0', + 'data-test-selector': 'chat-message-highlight' + }), + e('div', { + className: 'chat-line__message-container tw-relative' + }, out), + e('div', { + className: 'chat-line__reply-icon tw-absolute tw-border-radius-medium tw-c-background-base tw-elevation-1' + }, hover_actions) + ]; + } + + return e('div', { + className: `${cls}${msg.mentioned ? ' ffz-mentioned' : ''}${bg_css ? ' ffz-custom-color' : ''}`, + style: {backgroundColor: bg_css}, + 'data-room-id': msg.roomId, + 'data-room': msg.roomLogin, + 'data-user-id': msg.user.userID, + 'data-user': msg.user.userLogin && msg.user.userLogin.toLowerCase(), + onMouseOver: anim_hover ? t.chat.emotes.animHover : null, + onMouseOut: anim_hover ? t.chat.emotes.animLeave : null + }, out); + } + + /*cls.prototype.new_render = function() { try { + this._ffz_no_scan = true; + + const msg = t.chat.standardizeMessage(this.props.message), + reply_mode = t.chat.context.get('chat.replies.style'); + + let room = msg.roomLogin ? msg.roomLogin : msg.channel ? msg.channel.slice(1) : undefined, + room_id = msg.roomId ? msg.roomId : this.props.channelID; + + if ( ! room && room_id ) { + const r = t.chat.getRoom(room_id, null, true); + if ( r && r.login ) + room = msg.roomLogin = r.login; + } + + if ( ! room_id && room ) { + const r = t.chat.getRoom(null, room, true); + if ( r && r.id ) + room_id = msg.roomId = r.id; + } + + 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); + + if ( u ) { + u.moderator = this.props.isCurrentUserModerator; + u.staff = this.props.isCurrentUserStaff; + u.can_reply = reply_mode === 2 && can_reply; + } + + if ( ! this.ffz_open_reply ) + this.ffz_open_reply = this.ffzOpenReply.bind(this); + + if ( ! this.ffz_user_click_handler ) { + if ( this.props.onUsernameClick ) + this.ffz_user_click_handler = event => { + if ( this.isKeyboardEvent(event) && event.keyCode !== KEYS.Space && event.keyCode !== KEYS.Enter ) + return; + + const target = event.currentTarget, + ds = target && target.dataset; + let target_user = msg.user; + + if ( ds && ds.user ) { + try { + target_user = JSON.parse(ds.user); + } catch(err) { /* nothing~! * / } + } + + const fe = new FFZEvent({ + inst: this, + event, + message: msg, + user: target_user, + room: r + }); + + t.emit('chat:user-click', fe); + + if ( fe.defaultPrevented ) + return; + + this.props.onUsernameClick(target_user.login, 'chat_message', msg.id, target.getBoundingClientRect().bottom); + } + else + this.ffz_user_click_handler = this.openViewerCard || this.usernameClickHandler; //event => event.ctrlKey ? this.usernameClickHandler(event) : t.viewer_cards.openCard(r, user, event); + } + + // Do we have a special renderer? + if ( msg.ffz_type && t.line_types[msg.ffz_type] ) + return t.line_types[msg.ffz_type](msg, u, r, this, e); + + return this.ffz_old_render(); + + } catch(err) { + t.log.error(err); + t.log.capture(err, { + extra: { + props: this.props + } + }); + + try { + console.log('trying old 1'); + return old_render.call(this); + } catch(e2) { + t.log.error('An error in Twitch shit.', e2); + t.log.capture(e2, { + extra: { + props: this.props + } + }); + + return 'An error occurred rendering this chat line.'; + } + } };*/ + cls.prototype.render = function() { try { this._ffz_no_scan = true; @@ -932,7 +1173,19 @@ other {# messages were deleted by a moderator.} } }); - return old_render.call(this); + + try { + return old_render.call(this); + } catch(e2) { + t.log.error('An error in Twitch rendering.', e2); + t.log.capture(e2, { + extra: { + props: this.props + } + }); + + return 'An error occurred rendering this chat line.'; + } } } // Do this after a short delay to hopefully reduce the chance of React diff --git a/src/sites/twitch-twilight/modules/theme/index.js b/src/sites/twitch-twilight/modules/theme/index.js index 70ceaf58..31f1ed47 100644 --- a/src/sites/twitch-twilight/modules/theme/index.js +++ b/src/sites/twitch-twilight/modules/theme/index.js @@ -19,6 +19,8 @@ const COLORS = [ 0.08, 0.151, 0.212, 0.271, 0.31, 0.351 // 10-15 ]; +const NO_AUTO_DARK = `:root{color-scheme: only light}`; + 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-selected':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':10,'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','']}}, @@ -44,6 +46,21 @@ export default class ThemeEngine extends Module { this.should_enable = true; + + // Tweaks + + this.settings.add('theme.disable-auto-dark', { + default: false, + ui: { + path: 'Appearance > Theme >> Tweaks @{"sort": 10}', + title: 'Disable Auto Dark Theme', + component: 'setting-check-box', + description: 'Enabling this will attempt to disable a browser\'s built-in automatic dark theme conversion for websites, which is unnecessary on Twitch and can cause display issues. This may not apply depending on your web browser and settings.' + }, + changed: () => this.updateCSS() + }); + + // Font this.settings.add('theme.font.size', { @@ -321,6 +338,11 @@ export default class ThemeEngine extends Module { updateCSS() { //this.updateOldCSS(); + if ( this.settings.get('theme.disable-auto-dark') ) + this.css_tweaks.set('nodark', NO_AUTO_DARK); + else + this.css_tweaks.delete('nodark'); + this.css_tweaks.setVariable('border-color', 'var(--color-border-base)'); if ( ! this.settings.get('theme.can-dark') ) { diff --git a/src/sites/twitch-twilight/styles/color_normalizer.scss b/src/sites/twitch-twilight/styles/color_normalizer.scss index 80e44de5..79dc8fed 100644 --- a/src/sites/twitch-twilight/styles/color_normalizer.scss +++ b/src/sites/twitch-twilight/styles/color_normalizer.scss @@ -17,6 +17,7 @@ .ach-card--expanded .ach-card__inner, .room-upsell, .chat-room, + .video-chat, .qa-vod-chat, .video-card { background-color: var(--color-background-base) !important; diff --git a/src/utilities/constants.js b/src/utilities/constants.js index 5a80818c..bd0bcbdf 100644 --- a/src/utilities/constants.js +++ b/src/utilities/constants.js @@ -59,6 +59,7 @@ export const UPDATE_TOKEN_SETTINGS = [ 'chat.emotes.2x', 'chat.emotes.animated', 'chat.emoji.style', + 'chat.emoji.replace-joiner', 'chat.bits.stack', 'chat.rich.enabled', 'chat.rich.want-mid', diff --git a/src/utilities/rich_tokens.js b/src/utilities/rich_tokens.js index 066bd6fa..abd0ef5a 100644 --- a/src/utilities/rich_tokens.js +++ b/src/utilities/rich_tokens.js @@ -249,9 +249,17 @@ export default renderTokens; // Token Type: Reference // ============================================================================ +function resolveToken(token, ctx) { + if ( token?.type === 'ref' ) { + return ctx.fragments?.[token.name] ?? null; + } + + return token; +} + TOKEN_TYPES.ref = function(token, createElement, ctx) { const frag = ctx.fragments?.[token.name]; - if (frag ) + if ( frag ) return renderTokens(frag, createElement, ctx); } @@ -561,9 +569,10 @@ function header_vue(token, h, ctx) { ] }, content); - let imtok = token.sfw_image; - if ( token.image && canShowImage(token.image, ctx) ) - imtok = token.image; + let imtok = resolveToken(token.sfw_image, ctx); + const nsfw_token = resolveToken(token.image, ctx); + if ( nsfw_token && canShowImage(nsfw_token, ctx) ) + imtok = nsfw_token; if ( imtok ) { const aspect = imtok.aspect; @@ -651,9 +660,10 @@ function header_normal(token, createElement, ctx) { className: `tw-flex tw-full-width tw-overflow-hidden ${token.compact ? 'ffz--rich-header ffz--compact-header tw-align-items-center' : 'tw-justify-content-center tw-flex-column tw-flex-grow-1'}` }, content); - let imtok = token.sfw_image; - if ( token.image && canShowImage(token.image, ctx) ) - imtok = token.image; + let imtok = resolveToken(token.sfw_image, ctx); + const nsfw_token = resolveToken(token.image, ctx); + if ( nsfw_token && canShowImage(nsfw_token, ctx) ) + imtok = nsfw_token; if ( imtok ) { const aspect = imtok.aspect; @@ -839,6 +849,9 @@ TOKEN_TYPES.i18n = function(token, createElement, ctx) { // ============================================================================ TOKEN_TYPES.link = function(token, createElement, ctx) { + if ( token.content === undefined ) + token.content = token.url; + const content = renderTokens(token.content, createElement, ctx, token.markdown); const klass = [];