From fa9c418e2109b873a3a4b09f8b66f0642bfa128f Mon Sep 17 00:00:00 2001 From: Vjacheslav Trushkin Date: Mon, 2 Jan 2023 06:53:53 +0200 Subject: [PATCH] feat: replace emoji with SVGs (#129) (#584) Co-authored-by: Anthony Fu --- .gitignore | 1 + composables/content-parse.ts | 52 +++++++++------- composables/tiptap/emoji.ts | 26 ++++---- config/emojis.ts | 22 +++++++ package.json | 2 + plugins/setup-emojis.ts | 9 --- pnpm-lock.yaml | 60 ++++++------------- scripts/prepare.ts | 2 + styles/global.css | 22 ++++--- tests/__snapshots__/content-rich.test.ts.snap | 8 ++- tests/__snapshots__/html-parse.test.ts.snap | 33 ++++++++-- 11 files changed, 136 insertions(+), 101 deletions(-) create mode 100644 config/emojis.ts delete mode 100644 plugins/setup-emojis.ts diff --git a/.gitignore b/.gitignore index a48a3cd7..c4c69b12 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ dist .netlify/ public/shiki +public/emojis *~ *swp diff --git a/composables/content-parse.ts b/composables/content-parse.ts index c7be74c0..ae2213da 100644 --- a/composables/content-parse.ts +++ b/composables/content-parse.ts @@ -2,9 +2,8 @@ import type { Emoji } from 'masto' import type { Node } from 'ultrahtml' import { TEXT_NODE, parse, render, walkSync } from 'ultrahtml' -import createEmojiRegex from 'emoji-regex' - -export const EMOJI_REGEX = createEmojiRegex() +import { findAndReplaceEmojisInText } from '@iconify/utils' +import { emojiRegEx, getEmojiAttributes } from '../config/emojis' const decoder = process.client ? document.createElement('textarea') : null as any as HTMLTextAreaElement export function decodeHtml(text: string) { @@ -16,17 +15,17 @@ export function decodeHtml(text: string) { * Parse raw HTML form Mastodon server to AST, * with interop of custom emojis and inline Markdown syntax */ -export function parseMastodonHTML(html: string, customEmojis: Record = {}, markdown = true) { - let processed = html - // custom emojis - .replace(/:([\w-]+?):/g, (_, name) => { - const emoji = customEmojis[name] +export function parseMastodonHTML(html: string, customEmojis: Record = {}, markdown = true, forTiptap = false) { + // unicode emojis to images, but only if not converting HTML for Tiptap + let processed = forTiptap ? html : replaceUnicodeEmoji(html) - return emoji - ? `:${name}:` - : `:${name}:` - }) - .replace(EMOJI_REGEX, '') + // custom emojis + processed = processed.replace(/:([\w-]+?):/g, (_, name) => { + const emoji = customEmojis[name] + if (emoji) + return `:${name}:` + return `:${name}:` + }) if (markdown) { // handle code blocks @@ -66,8 +65,11 @@ export function parseMastodonHTML(html: string, customEmojis: Record = {}) { - const tree = parseMastodonHTML(html, customEmojis) + const tree = parseMastodonHTML(html, customEmojis, true, true) return render(tree) } @@ -118,12 +120,22 @@ export function treeToText(input: Node): string { if ('children' in input) body = (input.children as Node[]).map(n => treeToText(n)).join('') - // add spaces around emoji to prevent parsing errors: 2 or more consecutive emojis will not be parsed - if (input.name === 'img' && input.attributes.class?.includes('custom-emoji')) - return ` :${input.attributes['data-emoji-id']}: ` - - if (input.name === 'em-emoji') - return `${input.attributes.native}` + if (input.name === 'img') { + if (input.attributes.class?.includes('custom-emoji')) + return `:${input.attributes['data-emoji-id']}:` + if (input.attributes.class?.includes('iconify-emoji')) + return input.attributes.alt + } return pre + body + post } + +/** + * Replace unicode emojis with locally hosted images + */ +export function replaceUnicodeEmoji(html: string) { + return findAndReplaceEmojisInText(emojiRegEx, html, (match) => { + const attrs = getEmojiAttributes(match) + return `${attrs.alt}` + }) || html +} diff --git a/composables/tiptap/emoji.ts b/composables/tiptap/emoji.ts index a658a342..49f638ac 100644 --- a/composables/tiptap/emoji.ts +++ b/composables/tiptap/emoji.ts @@ -3,6 +3,7 @@ import { mergeAttributes, nodeInputRule, } from '@tiptap/core' +import { emojiRegEx, getEmojiAttributes } from '~/config/emojis' export const Emoji = Node.create({ name: 'em-emoji', @@ -14,35 +15,35 @@ export const Emoji = Node.create({ parseHTML() { return [ { - tag: 'em-emoji[native]', + tag: 'img.iconify-emoji', }, ] }, addAttributes() { return { - native: { + alt: { default: null, }, - fallback: { + src: { + default: null, + }, + class: { default: null, }, } }, renderHTML(args) { - return ['em-emoji', mergeAttributes(this.options.HTMLAttributes, args.HTMLAttributes)] + return ['img', mergeAttributes(this.options.HTMLAttributes, args.HTMLAttributes)] }, addCommands() { return { - insertEmoji: name => ({ commands }) => { + insertEmoji: code => ({ commands }) => { return commands.insertContent({ type: this.name, - attrs: { - native: name, - fallback: name, - }, + attrs: getEmojiAttributes(code), }) }, } @@ -50,14 +51,11 @@ export const Emoji = Node.create({ addInputRules() { const inputRule = nodeInputRule({ - find: EMOJI_REGEX, + find: emojiRegEx as RegExp, type: this.type, getAttributes: (match) => { const [native] = match - return { - native, - fallback: native, - } + return getEmojiAttributes(native) }, }) // Error catch for unsupported emoji diff --git a/config/emojis.ts b/config/emojis.ts new file mode 100644 index 00000000..6bc6dc82 --- /dev/null +++ b/config/emojis.ts @@ -0,0 +1,22 @@ +import { emojiFilename, emojiPrefix, emojiRegEx } from '@iconify-emoji/twemoji' +import type { EmojiRegexMatch } from '@iconify/utils/lib/emoji/replace/find' +import { getEmojiMatchesInText } from '@iconify/utils/lib/emoji/replace/find' + +// Re-export everything from package +export * from '@iconify-emoji/twemoji' + +// Package name +export const iconifyEmojiPackage = '@iconify-emoji/twemoji' + +export function getEmojiAttributes(input: EmojiRegexMatch | string) { + const match = typeof input === 'string' + ? getEmojiMatchesInText(emojiRegEx, input)?.[0] + : input + const file = emojiFilename(match) + const className = `iconify-emoji iconify-emoji--${emojiPrefix}${file.padding ? ' iconify-emoji-padded' : ''}` + return { + class: className, + src: `/emojis/${emojiPrefix}/${file.filename}`, + alt: match.match, + } +} diff --git a/package.json b/package.json index a46c058a..76abe212 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ }, "dependencies": { "@fnando/sparkline": "^0.3.10", + "@iconify-emoji/twemoji": "^1.0.2", + "@iconify/utils": "^2.0.7", "@nuxtjs/color-mode": "^3.2.0", "@tiptap/extension-character-count": "2.0.0-beta.204", "@tiptap/extension-code-block": "2.0.0-beta.204", diff --git a/plugins/setup-emojis.ts b/plugins/setup-emojis.ts deleted file mode 100644 index 6ef98ce7..00000000 --- a/plugins/setup-emojis.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default defineNuxtPlugin(() => { - if (process.server) - return - - const promise = import('@emoji-mart/data').then(r => r.default) - import('emoji-mart').then(r => r.init({ - data: () => promise, - })) -}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 871b733c..cb4e7b3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,12 +5,14 @@ specifiers: '@antfu/ni': ^0.18.8 '@emoji-mart/data': ^1.1.0 '@fnando/sparkline': ^0.3.10 + '@iconify-emoji/twemoji': ^1.0.2 '@iconify-json/carbon': ^1.1.11 '@iconify-json/logos': ^1.1.19 '@iconify-json/material-symbols': ^1.1.26 '@iconify-json/ph': ^1.1.3 '@iconify-json/ri': ^1.1.4 '@iconify-json/twemoji': ^1.1.7 + '@iconify/utils': ^2.0.7 '@nuxtjs/color-mode': ^3.2.0 '@nuxtjs/i18n': ^8.0.0-beta.7 '@pinia/nuxt': ^0.4.6 @@ -86,6 +88,8 @@ specifiers: dependencies: '@fnando/sparkline': 0.3.10 + '@iconify-emoji/twemoji': 1.0.2 + '@iconify/utils': 2.0.8 '@nuxtjs/color-mode': 3.2.0 '@tiptap/extension-character-count': 2.0.0-beta.204 '@tiptap/extension-code-block': 2.0.0-beta.204 @@ -273,7 +277,6 @@ packages: dependencies: execa: 5.1.1 find-up: 5.0.0 - dev: true /@antfu/ni/0.18.8: resolution: {integrity: sha512-0m++AudwQq+wWAz/Ax7g+sh/wFW51HHQ6BtPLsuTAsFIzWB/bv/0COwZE7BRS+u0nqMb6Ks6nlk6cY1TpPDwHg==} @@ -286,7 +289,6 @@ packages: /@antfu/utils/0.7.2: resolution: {integrity: sha512-vy9fM3pIxZmX07dL+VX1aZe7ynZ+YyB0jY+jE6r3hOK6GNY2t6W8rzpFC4tgpbXUYABkFQwgJq2XYXlxbXAI0g==} - dev: true /@apideck/better-ajv-errors/0.3.6_ajv@8.11.2: resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} @@ -1564,6 +1566,10 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true + /@iconify-emoji/twemoji/1.0.2: + resolution: {integrity: sha512-C4W6ov4BkDXiVU3GzyqyVo8SBbU21KivXnZERgAnrYZEKjuiI3JwPDnu9oVJPsUkNI/Q4SM8iVnXjGW6kxt9DQ==} + dev: false + /@iconify-json/carbon/1.1.11: resolution: {integrity: sha512-IHkHSNmTM6q6b8DuKSzd+AEMYPZywSxcb+37kZU7ywtcwsGen3aVEvWFykopIWjjwj3xdZ/5UdwJRqhZDQMlNg==} dependencies: @@ -1602,20 +1608,18 @@ packages: /@iconify/types/2.0.0: resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - dev: true - /@iconify/utils/2.0.5: - resolution: {integrity: sha512-UMT1WhBkr7oYggc69dFl/1RHE9XDisCrWaKXXQLpIccLCytHWZEX3247b/wR+sexYIKBSWs8YIKmMBe3g4oGCw==} + /@iconify/utils/2.0.8: + resolution: {integrity: sha512-e/1Rng92uxQTM+481EZaV1t7S03PFKIiWyc7io2/923DRUvOMcB1hwP6a2dvQ1Uf/0ncwXcwD+5bMTOkZlEdYw==} dependencies: '@antfu/install-pkg': 0.1.1 - '@antfu/utils': 0.5.2 + '@antfu/utils': 0.7.2 '@iconify/types': 2.0.0 debug: 4.3.4 kolorist: 1.6.0 local-pkg: 0.4.2 transitivePeerDependencies: - supports-color - dev: true /@intlify/bundle-utils/3.4.0_vue-i18n@9.3.0-beta.10: resolution: {integrity: sha512-2UQkqiSAOSPEHMGWlybqWm4G2K0X+FyYho5AwXz6QklSX1EY5EDmOSxZmwscn2qmKBnp6OYsme5kUrnN9xrWzQ==} @@ -1629,8 +1633,8 @@ packages: vue-i18n: optional: true dependencies: - '@intlify/message-compiler': 9.3.0-beta.12 - '@intlify/shared': 9.3.0-beta.12 + '@intlify/message-compiler': 9.3.0-beta.11 + '@intlify/shared': 9.3.0-beta.11 jsonc-eslint-parser: 1.4.1 source-map: 0.6.1 vue-i18n: 9.3.0-beta.10 @@ -1662,8 +1666,8 @@ packages: source-map: 0.6.1 dev: true - /@intlify/message-compiler/9.3.0-beta.12: - resolution: {integrity: sha512-A8/s7pb3v8nf6HG77qFPJntxgQKI9GXxGnkn7aO+b03/X/GkF/4WceDSAIk3i+yLeIgszeBn9GZ23tSg4sTEHA==} + /@intlify/message-compiler/9.3.0-beta.11: + resolution: {integrity: sha512-gGGfBGzM7JBXp1Q9gbDAy5jELz9ho3ILqnpxp2yp64+gkqohrqc2YXIvCdwZoc6AtKIh/Zmv4sWVqxkvMsBWtQ==} engines: {node: '>= 14'} dependencies: '@intlify/shared': 9.3.0-beta.11 @@ -1680,11 +1684,6 @@ packages: engines: {node: '>= 14'} dev: true - /@intlify/shared/9.3.0-beta.12: - resolution: {integrity: sha512-WsmaS54sA8xuwezPKpa/OMoaX1v2VF2fCgAmYS6prDr2ir0CkUFWPm9A8ilmxzv4nkS61/v8+vf4lGGkn5uBdA==} - engines: {node: '>= 14'} - dev: true - /@intlify/unplugin-vue-i18n/0.8.0_vue-i18n@9.3.0-beta.10: resolution: {integrity: sha512-bqMDYrbmV0oMLGHTdYMUXfcEsy2rPwQnGrQAg4gvw5FimvJfTQt3RliLVayT5ldOfeT2g0IUc/0t7LPeGrFUag==} engines: {node: '>= 14.16'} @@ -1701,7 +1700,7 @@ packages: optional: true dependencies: '@intlify/bundle-utils': 3.4.0_vue-i18n@9.3.0-beta.10 - '@intlify/shared': 9.3.0-beta.12 + '@intlify/shared': 9.3.0-beta.11 '@rollup/pluginutils': 4.2.1 '@vue/compiler-sfc': 3.2.45 debug: 4.3.4 @@ -2968,7 +2967,7 @@ packages: /@unocss/preset-icons/0.48.0: resolution: {integrity: sha512-3vro36gTkjEic5rO9BcUudby8tQ9ZRCduKZ1+4CKP0hKoB58nDm1QZM+kvWQ8RVN2xoSU9vWkHhx1RLl8miE0g==} dependencies: - '@iconify/utils': 2.0.5 + '@iconify/utils': 2.0.8 '@unocss/core': 0.48.0 ohmyfetch: 0.4.21 transitivePeerDependencies: @@ -4490,7 +4489,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /crypto-random-string/2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} @@ -5651,7 +5649,6 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - dev: true /execa/6.1.0: resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==} @@ -5770,7 +5767,6 @@ packages: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true /flat-cache/3.0.4: resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} @@ -5982,7 +5978,6 @@ packages: /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - dev: true /get-symbol-description/1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} @@ -6260,7 +6255,6 @@ packages: /human-signals/2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - dev: true /human-signals/3.0.1: resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} @@ -6569,7 +6563,6 @@ packages: /is-stream/2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - dev: true /is-stream/3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} @@ -6613,7 +6606,6 @@ packages: /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true /iso-639-1/2.1.15: resolution: {integrity: sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==} @@ -6822,7 +6814,6 @@ packages: /kolorist/1.6.0: resolution: {integrity: sha512-dLkz37Ab97HWMx9KTes3Tbi3D1ln9fCAy2zr2YVExJasDRPGRaKcoE4fycWNtnCAJfjFqe0cnY+f8KT2JePEXQ==} - dev: true /lazystream/1.0.1: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} @@ -6932,7 +6923,6 @@ packages: engines: {node: '>=10'} dependencies: p-locate: 5.0.0 - dev: true /lodash._reinterpolate/3.0.0: resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==} @@ -7115,7 +7105,6 @@ packages: /merge-stream/2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -7168,7 +7157,6 @@ packages: /mimic-fn/2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - dev: true /mimic-fn/4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} @@ -7467,7 +7455,6 @@ packages: engines: {node: '>=8'} dependencies: path-key: 3.1.1 - dev: true /npm-run-path/5.1.0: resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} @@ -7643,7 +7630,6 @@ packages: engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 - dev: true /onetime/6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} @@ -7721,7 +7707,6 @@ packages: engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 - dev: true /p-locate/4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} @@ -7735,7 +7720,6 @@ packages: engines: {node: '>=10'} dependencies: p-limit: 3.1.0 - dev: true /p-map/4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} @@ -7832,7 +7816,6 @@ packages: /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - dev: true /path-is-absolute/1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} @@ -7842,7 +7825,6 @@ packages: /path-key/3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - dev: true /path-key/4.0.0: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} @@ -8825,12 +8807,10 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 - dev: true /shebang-regex/3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - dev: true /shiki-es/0.1.2: resolution: {integrity: sha512-eqtfk8idlYlSLAn0gp0Ly2+FbKc2d78IddigHSS4iHAnpXoY2kdRzyFGZOdi6TvemYMnRhZBi1HsSqZc5eNKqg==} @@ -8854,7 +8834,6 @@ packages: /signal-exit/3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true /simple-git-hooks/2.8.1: resolution: {integrity: sha512-DYpcVR1AGtSfFUNzlBdHrQGPsOhuuEJ/FkmPOOlFysP60AHd3nsEpkGq/QEOdtUyT1Qhk7w9oLmFoMG+75BDog==} @@ -9109,7 +9088,6 @@ packages: /strip-final-newline/2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - dev: true /strip-final-newline/3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} @@ -10200,7 +10178,7 @@ packages: vue-router: optional: true dependencies: - '@intlify/shared': 9.3.0-beta.12 + '@intlify/shared': 9.3.0-beta.11 '@intlify/vue-i18n-bridge': 0.8.0_vue-i18n@9.3.0-beta.10 '@intlify/vue-router-bridge': 0.8.0 ufo: 1.0.1 @@ -10376,7 +10354,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true /wide-align/1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} @@ -10652,7 +10629,6 @@ packages: /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - dev: true /zip-stream/4.1.0: resolution: {integrity: sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==} diff --git a/scripts/prepare.ts b/scripts/prepare.ts index 85c96ba6..4a0e5c04 100644 --- a/scripts/prepare.ts +++ b/scripts/prepare.ts @@ -1,4 +1,5 @@ import { copy } from 'fs-extra' +import { emojiPrefix, iconifyEmojiPackage } from '../config/emojis' const dereference = process.platform === 'win32' ? true : undefined @@ -8,3 +9,4 @@ await copy('node_modules/shiki-es/dist/assets', 'public/shiki/', { }) await copy('node_modules/theme-vitesse/themes', 'public/shiki/themes', { dereference }) await copy('node_modules/theme-vitesse/themes', 'node_modules/shiki/themes', { overwrite: true, dereference }) +await copy(`node_modules/${iconifyEmojiPackage}/icons`, `public/emojis/${emojiPrefix}`, { overwrite: true, dereference }) diff --git a/styles/global.css b/styles/global.css index 29562a57..c750df4d 100644 --- a/styles/global.css +++ b/styles/global.css @@ -30,11 +30,6 @@ html { font-weight: 400; src: url(/fonts/homemade-apple-v18.ttf) format('truetype'); } -@font-face { - font-display: swap; - font-family: 'EmojiMart'; - src: url('/fonts/seguiemj.ttf') format('truetype'); -} * { scrollbar-color: #8885 var(--c-border); @@ -88,6 +83,19 @@ body { vertical-align: text-bottom; } +.iconify-emoji { + display: inline-block; + overflow: hidden; + max-height: 1.2em; + max-width: 1.2em; + vertical-align: text-bottom; + margin: 0 0.1em; +} + +.iconify-emoji-padded { + transform: scale(1.2); +} + .content-rich { line-height: calc(4 / 3 * 1em); overflow-wrap: break-word; @@ -195,10 +203,6 @@ html[dir="rtl"] .rtl-flip { em-emoji-picker { --border-radius: 0; } -em-emoji { - font-size: 1.2em; - line-height: 1em; -} footer { a { diff --git a/tests/__snapshots__/content-rich.test.ts.snap b/tests/__snapshots__/content-rich.test.ts.snap index 37b942ce..835b6a09 100644 --- a/tests/__snapshots__/content-rich.test.ts.snap +++ b/tests/__snapshots__/content-rich.test.ts.snap @@ -36,7 +36,13 @@ exports[`content-rich > empty 1`] = `""`; exports[`content-rich > link + mention 1`] = ` "

- Happy we’re now using + Happy + \\"πŸ€—\\" + we’re now using custom emoji > html 1`] = ` " `; -exports[`html-parse > custom emoji > text 1`] = `"Daniel Roe :nuxt:"`; +exports[`html-parse > custom emoji > text 1`] = `"Daniel Roe :nuxt:"`; exports[`html-parse > emojis > html 1`] = ` -" - - +"\\"πŸ‡«πŸ‡·\\" +\\"πŸ‘¨β€πŸ‘©β€πŸ‘¦\\" +\\"πŸ‘©β€πŸš’\\"\\"πŸ§‘πŸ½β€πŸš€\\" " `; @@ -87,7 +102,13 @@ code block exports[`html-parse > link + mention > html 1`] = ` "

- Happy we’re now using + Happy + \\"πŸ€—\\" + we’re now using