feat: replace emoji with SVGs (#129) (#584)

Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
This commit is contained in:
Vjacheslav Trushkin 2023-01-02 06:53:53 +02:00 committed by GitHub
parent 41c5f94fbf
commit fa9c418e21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 136 additions and 101 deletions

1
.gitignore vendored
View file

@ -10,6 +10,7 @@ dist
.netlify/
public/shiki
public/emojis
*~
*swp

View file

@ -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<string, Emoji> = {}, markdown = true) {
let processed = html
// custom emojis
.replace(/:([\w-]+?):/g, (_, name) => {
const emoji = customEmojis[name]
export function parseMastodonHTML(html: string, customEmojis: Record<string, Emoji> = {}, markdown = true, forTiptap = false) {
// unicode emojis to images, but only if not converting HTML for Tiptap
let processed = forTiptap ? html : replaceUnicodeEmoji(html)
return emoji
? `<img src="${emoji.url}" alt=":${name}:" class="custom-emoji" data-emoji-id="${name}" />`
: `:${name}:`
// custom emojis
processed = processed.replace(/:([\w-]+?):/g, (_, name) => {
const emoji = customEmojis[name]
if (emoji)
return `<img src="${emoji.url}" alt=":${name}:" class="custom-emoji" data-emoji-id="${name}" />`
return `:${name}:`
})
.replace(EMOJI_REGEX, '<em-emoji native="$&" fallback="$&" />')
if (markdown) {
// handle code blocks
@ -66,8 +65,11 @@ export function parseMastodonHTML(html: string, customEmojis: Record<string, Emo
return parse(processed)
}
/**
* Converts raw HTML form Mastodon server to HTML for Tiptap editor
*/
export function convertMastodonHTML(html: string, customEmojis: Record<string, Emoji> = {}) {
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'))
if (input.name === 'img') {
if (input.attributes.class?.includes('custom-emoji'))
return `:${input.attributes['data-emoji-id']}:`
if (input.name === 'em-emoji')
return `${input.attributes.native}`
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 `<img src="${attrs.src}" alt="${attrs.alt}" class="${attrs.class}" />`
}) || html
}

View file

@ -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

22
config/emojis.ts Normal file
View file

@ -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,
}
}

View file

@ -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",

View file

@ -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,
}))
})

View file

@ -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==}

View file

@ -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 })

View file

@ -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 {

View file

@ -36,7 +36,13 @@ exports[`content-rich > empty 1`] = `""`;
exports[`content-rich > link + mention 1`] = `
"<p>
Happy <em-emoji native=\\"🤗\\" fallback=\\"🤗\\"></em-emoji> were now using
Happy
<img
src=\\"/emojis/twemoji/1f917.svg\\"
alt=\\"🤗\\"
class=\\"iconify-emoji iconify-emoji--twemoji\\"
/>
were now using
<span class=\\"h-card\\"
><a
class=\\"u-url mention\\"

View file

@ -55,10 +55,25 @@ exports[`html-parse > custom emoji > html 1`] = `
exports[`html-parse > custom emoji > text 1`] = `"Daniel Roe :nuxt:"`;
exports[`html-parse > emojis > html 1`] = `
"<em-emoji native=\\"🇫🇷\\" fallback=\\"🇫🇷\\"></em-emoji>
<em-emoji native=\\"👨‍👩‍👦\\" fallback=\\"👨‍👩‍👦\\"></em-emoji>
<em-emoji native=\\"👩‍🚒\\" fallback=\\"👩‍🚒\\"></em-emoji
><em-emoji native=\\"🧑🏽‍🚀\\" fallback=\\"🧑🏽‍🚀\\"></em-emoji>
"<img
src=\\"/emojis/twemoji/1f1eb-1f1f7.svg\\"
alt=\\"🇫🇷\\"
class=\\"iconify-emoji iconify-emoji--twemoji\\"
/>
<img
src=\\"/emojis/twemoji/1f468-200d-1f469-200d-1f466.svg\\"
alt=\\"👨‍👩‍👦\\"
class=\\"iconify-emoji iconify-emoji--twemoji\\"
/>
<img
src=\\"/emojis/twemoji/1f469-200d-1f692.svg\\"
alt=\\"👩‍🚒\\"
class=\\"iconify-emoji iconify-emoji--twemoji\\"
/><img
src=\\"/emojis/twemoji/1f9d1-1f3fd-200d-1f680.svg\\"
alt=\\"🧑🏽‍🚀\\"
class=\\"iconify-emoji iconify-emoji--twemoji\\"
/>
"
`;
@ -87,7 +102,13 @@ code block
exports[`html-parse > link + mention > html 1`] = `
"<p>
Happy <em-emoji native=\\"🤗\\" fallback=\\"🤗\\"></em-emoji> were now using
Happy
<img
src=\\"/emojis/twemoji/1f917.svg\\"
alt=\\"🤗\\"
class=\\"iconify-emoji iconify-emoji--twemoji\\"
/>
were now using
<span class=\\"h-card\\"
><a
href=\\"https://mas.to/@vitest\\"