From 5c0c5e3758d497ce9db72dffc18afb3a8f173579 Mon Sep 17 00:00:00 2001 From: litruv Date: Tue, 21 Jul 2026 16:21:35 +1000 Subject: [PATCH] Fix jumbo emoji sizing being overridden by folds Text inherit. Render emoji-only messages without folds Text, force 100px on the container and emoticon elements, and pass jumbo through scaleSystemEmoji for plain-text bodies. --- src/app/components/editor/Elements.tsx | 2 +- .../components/message/MsgTypeRenderers.tsx | 9 +++- src/app/components/message/RenderBody.tsx | 6 ++- src/app/components/message/layout/Base.tsx | 43 +++++++++++++------ .../components/message/layout/layout.css.ts | 31 ++++++++++++- src/app/plugins/react-custom-html-parser.tsx | 14 ++++-- src/app/styles/CustomHtml.css.ts | 43 ++++++++++++++++--- src/app/utils/regex.ts | 2 +- 8 files changed, 120 insertions(+), 30 deletions(-) diff --git a/src/app/components/editor/Elements.tsx b/src/app/components/editor/Elements.tsx index 76ce876..648e573 100644 --- a/src/app/components/editor/Elements.tsx +++ b/src/app/components/editor/Elements.tsx @@ -100,7 +100,7 @@ function RenderEmoticonElement({ > {element.key.startsWith('mxc://') ? ( {element.shortcode} diff --git a/src/app/components/message/MsgTypeRenderers.tsx b/src/app/components/message/MsgTypeRenderers.tsx index 5459c48..4ad3a7d 100644 --- a/src/app/components/message/MsgTypeRenderers.tsx +++ b/src/app/components/message/MsgTypeRenderers.tsx @@ -69,6 +69,7 @@ export function BrokenContent() { type RenderBodyProps = { body: string; customBody?: string; + jumbo?: boolean; }; type MTextProps = { edited?: boolean; @@ -87,17 +88,19 @@ export function MText({ edited, content, renderBody, renderUrlsPreview, style }: : undefined; const urlsMatch = renderUrlsPreview && trimmedBody.match(URL_REG); const urls = urlsMatch ? [...new Set(urlsMatch)] : undefined; + const jumbo = isJumboEmoji(trimmedBody, trimmedCustomBody); return ( <> {renderBody({ body: trimmedBody, customBody: trimmedCustomBody, + jumbo, })} {edited && } @@ -165,17 +168,19 @@ export function MNotice({ edited, content, renderBody, renderUrlsPreview }: MNot : undefined; const urlsMatch = renderUrlsPreview && trimmedBody.match(URL_REG); const urls = urlsMatch ? [...new Set(urlsMatch)] : undefined; + const jumbo = isJumboEmoji(trimmedBody, trimmedCustomBody); return ( <> {renderBody({ body: trimmedBody, customBody: trimmedCustomBody, + jumbo, })} {edited && } diff --git a/src/app/components/message/RenderBody.tsx b/src/app/components/message/RenderBody.tsx index 6db9ee4..501ca30 100644 --- a/src/app/components/message/RenderBody.tsx +++ b/src/app/components/message/RenderBody.tsx @@ -9,6 +9,7 @@ import { highlightText, scaleSystemEmoji } from '../../plugins/react-custom-html type RenderBodyProps = { body: string; customBody?: string; + jumbo?: boolean; highlightRegex?: RegExp; htmlReactParserOptions: HTMLReactParserOptions; @@ -17,6 +18,7 @@ type RenderBodyProps = { export function RenderBody({ body, customBody, + jumbo = false, highlightRegex, htmlReactParserOptions, linkifyOpts, @@ -29,8 +31,8 @@ export function RenderBody({ return ( {highlightRegex - ? highlightText(highlightRegex, scaleSystemEmoji(body)) - : scaleSystemEmoji(body)} + ? highlightText(highlightRegex, scaleSystemEmoji(body, jumbo)) + : scaleSystemEmoji(body, jumbo)} ); } diff --git a/src/app/components/message/layout/Base.tsx b/src/app/components/message/layout/Base.tsx index 549b6f4..19d4d7f 100644 --- a/src/app/components/message/layout/Base.tsx +++ b/src/app/components/message/layout/Base.tsx @@ -29,16 +29,35 @@ export const UsernameBold = as<'b'>(({ as: AsUsernameBold = 'b', className, ...p )); export const MessageTextBody = as<'div', css.MessageTextBodyVariants & { notice?: boolean }>( - ({ as: asComp = 'div', className, preWrap, jumboEmoji, emote, notice, ...props }, ref) => ( - - ) + ({ as: asComp = 'div', className, preWrap, jumboEmoji, emote, notice, ...props }, ref) => { + const bodyClass = classNames( + css.MessageTextBody({ preWrap, jumboEmoji, emote }), + className + ); + + // Skip folds Text for jumbo — its DefaultReset sets font-size:inherit and wins the cascade. + if (jumboEmoji) { + return ( + + ); + } + + return ( + + ); + } ); diff --git a/src/app/components/message/layout/layout.css.ts b/src/app/components/message/layout/layout.css.ts index c1ff024..3242d40 100644 --- a/src/app/components/message/layout/layout.css.ts +++ b/src/app/components/message/layout/layout.css.ts @@ -248,15 +248,26 @@ export const MessageTextBody = recipe({ export type MessageTextBodyVariants = RecipeVariants; -// Prefer data-attr so sizing wins even if recipe class order fights folds Text sizes. +/** Jumbo emoji-only messages: 100×100px (system unicode + custom mx-emoticons). */ const jumboEmojiSelector = '[data-jumbo-emoji="true"]'; +globalStyle(jumboEmojiSelector, { + fontSize: `${toRem(100)} !important`, + lineHeight: `${toRem(100)} !important`, +}); + globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonBase}`, { height: `${toRem(100)} !important`, padding: '0 !important', verticalAlign: 'bottom', }); +globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonBaseJumbo}`, { + height: `${toRem(100)} !important`, + padding: '0 !important', + verticalAlign: 'bottom', +}); + globalStyle(`${jumboEmojiSelector} .${htmlCss.Emoticon.classNames.base}`, { fontSize: `${toRem(100)} !important`, height: `${toRem(100)} !important`, @@ -265,7 +276,23 @@ globalStyle(`${jumboEmojiSelector} .${htmlCss.Emoticon.classNames.base}`, { top: '0 !important', }); -globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonImg}`, { +globalStyle(`${jumboEmojiSelector} .${htmlCss.Emoticon.classNames.variants.jumbo.true}`, { + fontSize: `${toRem(100)} !important`, + height: `${toRem(100)} !important`, + minWidth: `${toRem(100)} !important`, + lineHeight: `${toRem(100)} !important`, + top: '0 !important', +}); + +globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonImg.classNames.base}`, { + height: `${toRem(100)} !important`, + width: `${toRem(100)} !important`, + maxHeight: `${toRem(100)} !important`, + maxWidth: `${toRem(100)} !important`, + objectFit: 'contain', +}); + +globalStyle(`${jumboEmojiSelector} .${htmlCss.EmoticonImg.classNames.variants.jumbo.true}`, { height: `${toRem(100)} !important`, width: `${toRem(100)} !important`, maxHeight: `${toRem(100)} !important`, diff --git a/src/app/plugins/react-custom-html-parser.tsx b/src/app/plugins/react-custom-html-parser.tsx index 96aa9f8..8e1eb0d 100644 --- a/src/app/plugins/react-custom-html-parser.tsx +++ b/src/app/plugins/react-custom-html-parser.tsx @@ -184,13 +184,19 @@ export const factoryRenderLinkifyWithMention = ( return render; }; -export const scaleSystemEmoji = (text: string): (string | JSX.Element)[] => +export const scaleSystemEmoji = (text: string, jumbo = false): (string | JSX.Element)[] => findAndReplace( text, EMOJI_REG_G, (match, pushIndex) => ( - - + + {match[0]} @@ -557,7 +563,7 @@ export const getReactCustomHtmlParser = ( return ( - + ); diff --git a/src/app/styles/CustomHtml.css.ts b/src/app/styles/CustomHtml.css.ts index 0dab8c6..80403e6 100644 --- a/src/app/styles/CustomHtml.css.ts +++ b/src/app/styles/CustomHtml.css.ts @@ -261,6 +261,15 @@ export const EmoticonBase = style([ }, ]); +export const EmoticonBaseJumbo = style([ + EmoticonBase, + { + height: toRem(100), + padding: 0, + verticalAlign: 'bottom', + }, +]); + export const Emoticon = recipe({ base: [ DefaultReset, @@ -285,16 +294,38 @@ export const Emoticon = recipe({ boxShadow: `0 0 0 ${config.borderWidth.B300} ${color.SurfaceVariant.OnContainer}`, }, }, + jumbo: { + true: { + fontSize: toRem(100), + height: toRem(100), + minWidth: toRem(100), + lineHeight: toRem(100), + top: 0, + }, + }, }, }); -export const EmoticonImg = style([ - DefaultReset, - { - height: '1em', - cursor: 'default', +export const EmoticonImg = recipe({ + base: [ + DefaultReset, + { + height: '1em', + cursor: 'default', + }, + ], + variants: { + jumbo: { + true: { + height: toRem(100), + width: toRem(100), + maxHeight: toRem(100), + maxWidth: toRem(100), + objectFit: 'contain', + }, + }, }, -]); +}); export const highlightText = style([ DefaultReset, diff --git a/src/app/utils/regex.ts b/src/app/utils/regex.ts index 5a82a46..81c7341 100644 --- a/src/app/utils/regex.ts +++ b/src/app/utils/regex.ts @@ -26,7 +26,7 @@ export const JUMBO_EMOJI_REG = new RegExp( ); /** Zero-width / format chars that break naive emoji-only matching. */ -const JUMBO_IGNORE_CHARS_REG = /[\u200B-\u200D\uFEFF\u00AD\u2060]/g; +const JUMBO_IGNORE_CHARS_REG = /[\u200B-\u200D\uFEFF\u00AD\u2060\u00A0]/g; const ONLY_MX_EMOTICON_HTML_REG = /^(?:\s|)*((?:]*\bdata-mx-emoticon\b[^>]*\/?>)(?:\s|)*){1,10}$/i;