diff --git a/CHANGELOG.md b/CHANGELOG.md index e42e2206a3..861b0cea6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to - 🐛(frontend) make summary button fixed to remain visible during scroll #1581 - 🐛(frontend) fix pdf embed to use full width #1526 - 🐛(pdf) fix table cell alignment issue in exported documents #1582 +- 🐛(frontend) prevent duplicate emoji when used as first character in t… #1595 ## [3.9.0] - 2025-11-10 diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx index 33905caa73..d99a3ee287 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx @@ -122,15 +122,22 @@ const DocTitleInput = ({ doc }: DocTitleProps) => { if (isTopRoot) { const sanitizedTitle = updateDocTitle(doc, inputText); setTitleDisplay(sanitizedTitle); + return sanitizedTitle; } else { - const sanitizedTitle = updateDocTitle( - doc, - emoji ? `${emoji} ${inputText}` : inputText, - ); + const { emoji: pastedEmoji } = getEmojiAndTitle(inputText); + const textPreservingPastedEmoji = pastedEmoji + ? `\u200B${inputText}` + : inputText; + const finalTitle = emoji + ? `${emoji} ${textPreservingPastedEmoji}` + : textPreservingPastedEmoji; + + const sanitizedTitle = updateDocTitle(doc, finalTitle); const { titleWithoutEmoji: sanitizedTitleWithoutEmoji } = getEmojiAndTitle(sanitizedTitle); setTitleDisplay(sanitizedTitleWithoutEmoji); + return sanitizedTitleWithoutEmoji; } }, [updateDocTitle, doc, emoji, isTopRoot], diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts b/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts index 2d43b68fc2..9e7bef554d 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts @@ -26,12 +26,14 @@ export const getEmojiAndTitle = (title: string) => { // Use emoji-regex library for comprehensive emoji detection compatible with ES5 const regex = emojiRegex(); - // Check if the title starts with an emoji - const match = title.match(regex); + // Ignore leading spaces when checking for a leading emoji + const leadingSpacesLength = title.match(/^\s+/)?.[0]?.length ?? 0; + const trimmedStart = title.slice(leadingSpacesLength); + const match = trimmedStart.match(regex); - if (match && title.startsWith(match[0])) { + if (match && trimmedStart.startsWith(match[0])) { const emoji = match[0]; - const titleWithoutEmoji = title.substring(emoji.length).trim(); + const titleWithoutEmoji = trimmedStart.substring(emoji.length).trim(); return { emoji, titleWithoutEmoji }; }