feat: show emoji tooltip (#2485)
parent
74138a9a58
commit
c0bb6e293c
|
@ -2,6 +2,7 @@ node_modules
|
||||||
*.log
|
*.log
|
||||||
dist
|
dist
|
||||||
.output
|
.output
|
||||||
|
.pnpm-store
|
||||||
.nuxt
|
.nuxt
|
||||||
.env
|
.env
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { as, alt, dataEmojiId } = $defineProps<{
|
||||||
|
as: string
|
||||||
|
alt?: string
|
||||||
|
dataEmojiId?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
let title = $ref<string | undefined>()
|
||||||
|
|
||||||
|
if (alt) {
|
||||||
|
if (alt.startsWith(':')) {
|
||||||
|
title = alt.replace(/:/g, '')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
import('node-emoji').then(({ find }) => {
|
||||||
|
title = find(alt)?.key.replace(/_/g, ' ')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it has a data-emoji-id, use that as the title instead
|
||||||
|
if (dataEmojiId)
|
||||||
|
title = dataEmojiId
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component :is="as" v-bind="$attrs" :alt="alt" :data-emoji-id="dataEmojiId" :title="title">
|
||||||
|
<slot />
|
||||||
|
</component>
|
||||||
|
</template>
|
|
@ -6,6 +6,7 @@ import { RouterLink } from 'vue-router'
|
||||||
import { decode } from 'tiny-decode'
|
import { decode } from 'tiny-decode'
|
||||||
import type { ContentParseOptions } from './content-parse'
|
import type { ContentParseOptions } from './content-parse'
|
||||||
import { parseMastodonHTML } from './content-parse'
|
import { parseMastodonHTML } from './content-parse'
|
||||||
|
import Emoji from '~/components/emoji/Emoji.vue'
|
||||||
import ContentCode from '~/components/content/ContentCode.vue'
|
import ContentCode from '~/components/content/ContentCode.vue'
|
||||||
import ContentMentionGroup from '~/components/content/ContentMentionGroup.vue'
|
import ContentMentionGroup from '~/components/content/ContentMentionGroup.vue'
|
||||||
import AccountHoverWrapper from '~/components/account/AccountHoverWrapper.vue'
|
import AccountHoverWrapper from '~/components/account/AccountHoverWrapper.vue'
|
||||||
|
@ -19,8 +20,11 @@ function getTextualAstComponents(astChildren: Node[]): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw HTML to VNodes
|
* Raw HTML to VNodes.
|
||||||
*/
|
*
|
||||||
|
* @param content HTML content.
|
||||||
|
* @param options Options.
|
||||||
|
*/
|
||||||
export function contentToVNode(
|
export function contentToVNode(
|
||||||
content: string,
|
content: string,
|
||||||
options?: ContentParseOptions,
|
options?: ContentParseOptions,
|
||||||
|
@ -43,6 +47,17 @@ export function nodeToVNode(node: Node): VNode | string | null {
|
||||||
if (node.name === 'mention-group')
|
if (node.name === 'mention-group')
|
||||||
return h(ContentMentionGroup, node.attributes, () => node.children.map(treeToVNode))
|
return h(ContentMentionGroup, node.attributes, () => node.children.map(treeToVNode))
|
||||||
|
|
||||||
|
// add tooltip to emojis
|
||||||
|
if (node.name === 'picture' || (node.name === 'img' && node.attributes?.alt)) {
|
||||||
|
const props = node.attributes ?? {}
|
||||||
|
props.as = node.name
|
||||||
|
return h(
|
||||||
|
Emoji,
|
||||||
|
props,
|
||||||
|
() => node.children.map(treeToVNode),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if ('children' in node) {
|
if ('children' in node) {
|
||||||
if (node.name === 'a' && (node.attributes.href?.startsWith('/') || node.attributes.href?.startsWith('.'))) {
|
if (node.name === 'a' && (node.attributes.href?.startsWith('/') || node.attributes.href?.startsWith('.'))) {
|
||||||
node.attributes.to = node.attributes.href
|
node.attributes.to = node.attributes.href
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"lru-cache": "^10.0.0",
|
"lru-cache": "^10.0.0",
|
||||||
"masto": "^5.11.3",
|
"masto": "^5.11.3",
|
||||||
|
"node-emoji": "^2.1.3",
|
||||||
"nuxt-security": "^0.13.1",
|
"nuxt-security": "^0.13.1",
|
||||||
"page-lifecycle": "^0.1.2",
|
"page-lifecycle": "^0.1.2",
|
||||||
"pinia": "^2.1.4",
|
"pinia": "^2.1.4",
|
||||||
|
|
|
@ -164,6 +164,9 @@ importers:
|
||||||
masto:
|
masto:
|
||||||
specifier: ^5.11.3
|
specifier: ^5.11.3
|
||||||
version: 5.11.3
|
version: 5.11.3
|
||||||
|
node-emoji:
|
||||||
|
specifier: ^2.1.3
|
||||||
|
version: 2.1.3
|
||||||
nuxt-security:
|
nuxt-security:
|
||||||
specifier: ^0.13.1
|
specifier: ^0.13.1
|
||||||
version: 0.13.1(patch_hash=bd6cmp7ukwwiwrxafbbotwkihe)(rollup@2.79.1)
|
version: 0.13.1(patch_hash=bd6cmp7ukwwiwrxafbbotwkihe)(rollup@2.79.1)
|
||||||
|
@ -4141,6 +4144,11 @@ packages:
|
||||||
/@sinclair/typebox@0.27.8:
|
/@sinclair/typebox@0.27.8:
|
||||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||||
|
|
||||||
|
/@sindresorhus/is@4.6.0:
|
||||||
|
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@sindresorhus/merge-streams@1.0.0:
|
/@sindresorhus/merge-streams@1.0.0:
|
||||||
resolution: {integrity: sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==}
|
resolution: {integrity: sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
@ -6865,6 +6873,11 @@ packages:
|
||||||
snake-case: 3.0.4
|
snake-case: 3.0.4
|
||||||
tslib: 2.6.0
|
tslib: 2.6.0
|
||||||
|
|
||||||
|
/char-regex@1.0.2:
|
||||||
|
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/char-regex@2.0.1:
|
/char-regex@2.0.1:
|
||||||
resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==}
|
resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==}
|
||||||
engines: {node: '>=12.20'}
|
engines: {node: '>=12.20'}
|
||||||
|
@ -7573,6 +7586,10 @@ packages:
|
||||||
/emoji-regex@9.2.2:
|
/emoji-regex@9.2.2:
|
||||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||||
|
|
||||||
|
/emojilib@2.4.0:
|
||||||
|
resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/emoticon@4.0.1:
|
/emoticon@4.0.1:
|
||||||
resolution: {integrity: sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==}
|
resolution: {integrity: sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -10773,6 +10790,16 @@ packages:
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/node-emoji@2.1.3:
|
||||||
|
resolution: {integrity: sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
dependencies:
|
||||||
|
'@sindresorhus/is': 4.6.0
|
||||||
|
char-regex: 1.0.2
|
||||||
|
emojilib: 2.4.0
|
||||||
|
skin-tone: 2.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/node-fetch-native@1.4.1:
|
/node-fetch-native@1.4.1:
|
||||||
resolution: {integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==}
|
resolution: {integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==}
|
||||||
|
|
||||||
|
@ -13007,6 +13034,13 @@ packages:
|
||||||
/sisteransi@1.0.5:
|
/sisteransi@1.0.5:
|
||||||
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
||||||
|
|
||||||
|
/skin-tone@2.0.0:
|
||||||
|
resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
unicode-emoji-modifier-base: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/slash@3.0.0:
|
/slash@3.0.0:
|
||||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -13837,6 +13871,11 @@ packages:
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/unicode-emoji-modifier-base@1.0.0:
|
||||||
|
resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/unicode-match-property-ecmascript@2.0.0:
|
/unicode-match-property-ecmascript@2.0.0:
|
||||||
resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
|
resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
|
@ -58,7 +58,7 @@ exports[`content-rich > code frame no lang 1`] = `"<p><pre class="code-block">he
|
||||||
|
|
||||||
exports[`content-rich > custom emoji 1`] = `
|
exports[`content-rich > custom emoji 1`] = `
|
||||||
"Daniel Roe
|
"Daniel Roe
|
||||||
<picture alt=":nuxt:" class="custom-emoji" data-emoji-id="nuxt"
|
<picture class="custom-emoji" alt=":nuxt:" data-emoji-id="nuxt" title="nuxt"
|
||||||
><source
|
><source
|
||||||
srcset="
|
srcset="
|
||||||
https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png
|
https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png
|
||||||
|
@ -67,6 +67,7 @@ exports[`content-rich > custom emoji 1`] = `
|
||||||
<img
|
<img
|
||||||
src="https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png"
|
src="https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png"
|
||||||
alt=":nuxt:"
|
alt=":nuxt:"
|
||||||
|
title="nuxt"
|
||||||
/></picture>
|
/></picture>
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
@ -129,8 +130,8 @@ exports[`content-rich > link + mention 1`] = `
|
||||||
Happy
|
Happy
|
||||||
<img
|
<img
|
||||||
src="/emojis/twemoji/1f917.svg"
|
src="/emojis/twemoji/1f917.svg"
|
||||||
alt="🤗"
|
|
||||||
class="iconify-emoji iconify-emoji--twemoji"
|
class="iconify-emoji iconify-emoji--twemoji"
|
||||||
|
alt="🤗"
|
||||||
/>
|
/>
|
||||||
we’re now using
|
we’re now using
|
||||||
<span class="h-card"
|
<span class="h-card"
|
||||||
|
|
Loading…
Reference in New Issue