From c0bb6e293cca396f77703fdf28ddb25093a39852 Mon Sep 17 00:00:00 2001 From: Shinigami Date: Fri, 22 Dec 2023 13:16:46 +0100 Subject: [PATCH] feat: show emoji tooltip (#2485) --- .gitignore | 1 + components/emoji/Emoji.vue | 30 ++++++++++++++ composables/content-render.ts | 19 ++++++++- package.json | 1 + pnpm-lock.yaml | 39 +++++++++++++++++++ .../__snapshots__/content-rich.test.ts.snap | 5 ++- 6 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 components/emoji/Emoji.vue diff --git a/.gitignore b/.gitignore index b61e1356..5e134baa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules *.log dist .output +.pnpm-store .nuxt .env .DS_Store diff --git a/components/emoji/Emoji.vue b/components/emoji/Emoji.vue new file mode 100644 index 00000000..15bbf07d --- /dev/null +++ b/components/emoji/Emoji.vue @@ -0,0 +1,30 @@ + + + diff --git a/composables/content-render.ts b/composables/content-render.ts index d0302031..bded3767 100644 --- a/composables/content-render.ts +++ b/composables/content-render.ts @@ -6,6 +6,7 @@ import { RouterLink } from 'vue-router' import { decode } from 'tiny-decode' import type { ContentParseOptions } from './content-parse' import { parseMastodonHTML } from './content-parse' +import Emoji from '~/components/emoji/Emoji.vue' import ContentCode from '~/components/content/ContentCode.vue' import ContentMentionGroup from '~/components/content/ContentMentionGroup.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( content: string, options?: ContentParseOptions, @@ -43,6 +47,17 @@ export function nodeToVNode(node: Node): VNode | string | null { if (node.name === 'mention-group') 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 (node.name === 'a' && (node.attributes.href?.startsWith('/') || node.attributes.href?.startsWith('.'))) { node.attributes.to = node.attributes.href diff --git a/package.json b/package.json index 439e3ede..e30fe632 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "js-yaml": "^4.1.0", "lru-cache": "^10.0.0", "masto": "^5.11.3", + "node-emoji": "^2.1.3", "nuxt-security": "^0.13.1", "page-lifecycle": "^0.1.2", "pinia": "^2.1.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c67cd8c..673424e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -164,6 +164,9 @@ importers: masto: specifier: ^5.11.3 version: 5.11.3 + node-emoji: + specifier: ^2.1.3 + version: 2.1.3 nuxt-security: specifier: ^0.13.1 version: 0.13.1(patch_hash=bd6cmp7ukwwiwrxafbbotwkihe)(rollup@2.79.1) @@ -4141,6 +4144,11 @@ packages: /@sinclair/typebox@0.27.8: 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: resolution: {integrity: sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==} engines: {node: '>=18'} @@ -6865,6 +6873,11 @@ packages: snake-case: 3.0.4 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: resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==} engines: {node: '>=12.20'} @@ -7573,6 +7586,10 @@ packages: /emoji-regex@9.2.2: 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: resolution: {integrity: sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==} dev: true @@ -10773,6 +10790,16 @@ packages: lodash: 4.17.21 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: resolution: {integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==} @@ -13007,6 +13034,13 @@ packages: /sisteransi@1.0.5: 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: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -13837,6 +13871,11 @@ packages: engines: {node: '>=4'} 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: resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} engines: {node: '>=4'} diff --git a/tests/nuxt/__snapshots__/content-rich.test.ts.snap b/tests/nuxt/__snapshots__/content-rich.test.ts.snap index 3e4e5b2e..db4639a9 100644 --- a/tests/nuxt/__snapshots__/content-rich.test.ts.snap +++ b/tests/nuxt/__snapshots__/content-rich.test.ts.snap @@ -58,7 +58,7 @@ exports[`content-rich > code frame no lang 1`] = `"

he
 
 exports[`content-rich > custom emoji 1`] = `
 "Daniel Roe
-
 "
 `;
@@ -129,8 +130,8 @@ exports[`content-rich > link + mention 1`] = `
   Happy
   🤗
   we’re now using