mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-04-20 15:45:44 +00:00
feat(conversation-viewer): update message rendering with single emoji detection and dynamic font sizing for improved display
This commit is contained in:
@@ -818,10 +818,11 @@
|
||||
'markdown-content--outbound-solid':
|
||||
chatItem.is_outbound && !isThemeOutboundBubble(chatItem),
|
||||
'markdown-content--inbound': !chatItem.is_outbound,
|
||||
'markdown-content--single-emoji': messageMarkdownSingleEmoji(chatItem),
|
||||
}"
|
||||
:style="{
|
||||
'font-family': 'inherit',
|
||||
'font-size': (config?.message_font_size || 14) + 'px',
|
||||
'font-size': messageMarkdownFontSizePx(chatItem) + 'px',
|
||||
}"
|
||||
@click="handleMessageClick"
|
||||
v-html="renderMarkdown(chatItem.lxmf_message.content)"
|
||||
@@ -2989,6 +2990,26 @@ export default {
|
||||
renderMarkdown(text) {
|
||||
return MarkdownRenderer.render(text);
|
||||
},
|
||||
messageMarkdownSingleEmoji(chatItem) {
|
||||
const c = chatItem?.lxmf_message?.content;
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
if (this.getParsedItems(chatItem)?.isOnlyPaperMessage) {
|
||||
return false;
|
||||
}
|
||||
if (this.shouldHideAutoImageCaption(chatItem)) {
|
||||
return false;
|
||||
}
|
||||
return MarkdownRenderer.isSingleEmojiMessage(c);
|
||||
},
|
||||
messageMarkdownFontSizePx(chatItem) {
|
||||
const base = Number(this.config?.message_font_size) || 14;
|
||||
if (this.messageMarkdownSingleEmoji(chatItem)) {
|
||||
return Math.round(base * 2.75);
|
||||
}
|
||||
return base;
|
||||
},
|
||||
handleMessageClick(event) {
|
||||
const nomadnetLink = event.target.closest(".nomadnet-link");
|
||||
if (nomadnetLink) {
|
||||
@@ -5724,6 +5745,15 @@ export default {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-content--single-emoji {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.markdown-content--single-emoji :deep(p) {
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.markdown-content :deep(strong) {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,30 @@ export default class MarkdownRenderer {
|
||||
return processed_parts.join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the body is only a single emoji (after markdown strip), for large bubble rendering.
|
||||
*/
|
||||
static isSingleEmojiMessage(raw) {
|
||||
if (raw == null || typeof raw !== "string") {
|
||||
return false;
|
||||
}
|
||||
let plain = MarkdownRenderer.strip(raw);
|
||||
plain = plain.replace(/\s+/g, "");
|
||||
if (!plain) {
|
||||
return false;
|
||||
}
|
||||
if (typeof Intl === "undefined" || typeof Intl.Segmenter !== "function") {
|
||||
return false;
|
||||
}
|
||||
const seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
||||
const clusters = [...seg.segment(plain)].map((s) => s.segment);
|
||||
if (clusters.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
const g = clusters[0];
|
||||
return /\p{Extended_Pictographic}/u.test(g) || /\p{Emoji}/u.test(g);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips markdown from text for previews.
|
||||
*/
|
||||
|
||||
@@ -226,6 +226,22 @@ describe("MarkdownRenderer.js", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("isSingleEmojiMessage", () => {
|
||||
it("is true for one emoji and false for text or multiple emojis", () => {
|
||||
expect(MarkdownRenderer.isSingleEmojiMessage("\u{1F600}")).toBe(true);
|
||||
expect(MarkdownRenderer.isSingleEmojiMessage(" \u{1F600} ")).toBe(true);
|
||||
expect(MarkdownRenderer.isSingleEmojiMessage("**\u{1F600}**")).toBe(true);
|
||||
expect(MarkdownRenderer.isSingleEmojiMessage("\u{1F600}\u{1F600}")).toBe(false);
|
||||
expect(MarkdownRenderer.isSingleEmojiMessage("hi")).toBe(false);
|
||||
expect(MarkdownRenderer.isSingleEmojiMessage("\u{1F600} a")).toBe(false);
|
||||
});
|
||||
|
||||
it("treats ZWJ family and skin tone as a single emoji", () => {
|
||||
expect(MarkdownRenderer.isSingleEmojiMessage("\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}")).toBe(true);
|
||||
expect(MarkdownRenderer.isSingleEmojiMessage("\u{1F44D}\u{1F3FD}")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("strip", () => {
|
||||
it("strips markdown correctly", () => {
|
||||
const md = "# Header\n**Bold** *Italic* `code` ```\nblock\n```";
|
||||
|
||||
Reference in New Issue
Block a user