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://') ? (
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;