From 5e9f6a90df9c999d36283d2ba9eb8e23ccfd7d7b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 4 Nov 2023 18:27:22 +0900 Subject: [PATCH] =?UTF-8?q?enhance(frontend):=20=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E5=86=85=E3=81=AE=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92=E3=82=AF=E3=83=AA=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=81=99=E3=82=8B=E3=81=93=E3=81=A8=E3=81=A7=E3=80=81?= =?UTF-8?q?=E3=82=B3=E3=83=94=E3=83=BC=E3=81=8A=E3=82=88=E3=81=B3=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=8C=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/components/MkNote.vue | 22 ++++++++-- .../src/components/MkNoteDetailed.vue | 6 +-- .../frontend/src/components/MkNoteSimple.vue | 2 +- .../frontend/src/components/MkNoteSub.vue | 2 +- .../src/components/MkSubNoteContent.vue | 2 +- .../frontend/src/components/MkUserInfo.vue | 2 +- .../frontend/src/components/MkUserPopup.vue | 2 +- .../src/components/MkUserSetupDialog.User.vue | 2 +- .../src/components/global/MkCustomEmoji.vue | 43 ++++++++++++++++++- .../global/MkMisskeyFlavoredMarkdown.ts | 5 ++- .../src/components/page/page.text.vue | 2 +- packages/frontend/src/pages/channel.vue | 2 +- packages/frontend/src/pages/clip.vue | 2 +- packages/frontend/src/pages/user/home.vue | 4 +- .../frontend/src/pages/welcome.timeline.vue | 2 +- 18 files changed, 82 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ad55752b1..4038755124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ - Enhance: プラグインã§`Plugin:register_note_view_interruptor`を用ã„ã¦noteã®ä»£ã‚ã‚Šã«nullã‚’è¿”å´ã™ã‚‹ã“ã¨ã§ãƒŽãƒ¼ãƒˆã‚’éžè¡¨ç¤ºã«ã§ãるよã†ã«ãªã‚Šã¾ã—㟠- Enhance: AiScript関数`Mk:nyaize()`ãŒè¿½åŠ ã•ã‚Œã¾ã—㟠- Enhance: æƒ…å ±â†’ãƒ„ãƒ¼ãƒ« ã¯ãƒŠãƒ“ゲーションãƒãƒ¼ã«ãƒ„ールã¨ã—ã¦ç‹¬ç«‹ã—ãŸé …ç›®ã«ãªã‚Šã¾ã—㟠+- Enhance: ノート内ã®ã‚«ã‚¹ã‚¿ãƒ 絵文å—をクリックã™ã‚‹ã“ã¨ã§ã€ã‚³ãƒ”ーãŠã‚ˆã³ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ãŒã§ãるよã†ã« - Enhance: ãã®ä»–ç´°ã‹ãªãƒ–ラッシュアップ - Fix: 投稿フォームã§ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å¤‰æ›´ãŒãƒ—レビューã«åæ˜ ã•ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ - Fix: ユーザーページ㮠ノート > ファイル付ã タブã«ãƒªãƒ—ライãŒè¡¨ç¤ºã•ã‚Œã¦ã—ã¾ã† diff --git a/locales/index.d.ts b/locales/index.d.ts index 50b11acc06..aedaaa9f7c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1160,6 +1160,7 @@ export interface Locale { "useGroupedNotifications": string; "signupPendingError": string; "cwNotationRequired": string; + "doReaction": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index de4e8ce2b3..6ecebfc393 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1157,6 +1157,7 @@ disableStreamingTimeline: "タイムラインã®ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ æ›´æ–°ã‚’ç„¡ useGroupedNotifications: "通知をグルーピングã—ã¦è¡¨ç¤ºã™ã‚‹" signupPendingError: "メールアドレスã®ç¢ºèªä¸ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚リンクã®æœ‰åŠ¹æœŸé™ãŒåˆ‡ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚" cwNotationRequired: "ã€Œå†…å®¹ã‚’éš ã™ã€ãŒã‚ªãƒ³ã®å ´åˆã¯æ³¨é‡ˆã®è¨˜è¿°ãŒå¿…è¦ã§ã™ã€‚" +doReaction: "リアクションã™ã‚‹" _announcement: forExistingUsers: "æ—¢å˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 30a68f38f2..0ae3423a21 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -53,19 +53,28 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> <div style="container-type: inline-size;"> <p v-if="appearNote.cw != null" :class="$style.cw"> - <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'account'" :i="$i"/> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'account'"/> <MkCwButton v-model="showContent" :note="appearNote" style="margin: 4px 0;"/> </p> <div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]"> <div :class="$style.text"> <span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> <MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> - <Mfm v-if="appearNote.text" :parsedNodes="parsed" :text="appearNote.text" :author="appearNote.user" :nyaize="'account'" :i="$i" :emojiUrls="appearNote.emojis"/> + <Mfm + v-if="appearNote.text" + :parsedNodes="parsed" + :text="appearNote.text" + :author="appearNote.user" + :nyaize="'account'" + :emojiUrls="appearNote.emojis" + :enableEmojiMenu="true" + :enableEmojiMenuReaction="true" + /> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> <div v-else> <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'account'" :i="$i" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'account'" :emojiUrls="appearNote.emojis"/> </div> </div> </div> @@ -242,6 +251,13 @@ const keymap = { 's': () => showContent.value !== showContent.value, }; +provide('react', (reaction: string) => { + os.api('notes/reactions/create', { + noteId: appearNote.id, + reaction: reaction, + }); +}); + if (props.mock) { watch(() => props.note, (to) => { note = deepClone(to); diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 9e9b1035d7..ff2941344e 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -67,19 +67,19 @@ SPDX-License-Identifier: AGPL-3.0-only </header> <div :class="$style.noteContent"> <p v-if="appearNote.cw != null" :class="$style.cw"> - <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'account'" :i="$i"/> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'account'"/> <MkCwButton v-model="showContent" :note="appearNote"/> </p> <div v-show="appearNote.cw == null || showContent"> <span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> <MkA v-if="appearNote.replyId" :class="$style.noteReplyTarget" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> - <Mfm v-if="appearNote.text" :parsedNodes="parsed" :text="appearNote.text" :author="appearNote.user" :nyaize="'account'" :i="$i" :emojiUrls="appearNote.emojis"/> + <Mfm v-if="appearNote.text" :parsedNodes="parsed" :text="appearNote.text" :author="appearNote.user" :nyaize="'account'" :emojiUrls="appearNote.emojis"/> <a v-if="appearNote.renote != null" :class="$style.rn">RN:</a> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> <div v-else> <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'account'" :i="$i" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'account'" :emojiUrls="appearNote.emojis"/> </div> </div> <div v-if="appearNote.files.length > 0"> diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index dc401a7ecb..28b00af246 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkNoteHeader :class="$style.header" :note="note" :mini="true"/> <div> <p v-if="note.cw != null" :class="$style.cw"> - <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'account'" :i="$i" :emojiUrls="note.emojis"/> + <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'account'" :emojiUrls="note.emojis"/> <MkCwButton v-model="showContent" :note="note"/> </p> <div v-show="note.cw == null || showContent"> diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index 3cc8767007..f61b963d9b 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkNoteHeader :class="$style.header" :note="note" :mini="true"/> <div> <p v-if="note.cw != null" :class="$style.cw"> - <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'account'" :i="$i"/> + <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'account'"/> <MkCwButton v-model="showContent" :note="note"/> </p> <div v-show="note.cw == null || showContent"> diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index 51dabee161..e9f2b838d2 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> <span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deleted }})</span> <MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> - <Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :emojiUrls="note.emojis"/> + <Mfm v-if="note.text" :text="note.text" :author="note.user" :emojiUrls="note.emojis"/> <MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA> </div> <details v-if="note.files.length > 0"> diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue index c13ef60f3b..eaebbf03e7 100644 --- a/packages/frontend/src/components/MkUserInfo.vue +++ b/packages/frontend/src/components/MkUserInfo.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="$i && $i.id !== user.id && user.isFollowed" :class="$style.followed">{{ i18n.ts.followsYou }}</span> <div :class="$style.description"> <div v-if="user.description" :class="$style.mfm"> - <Mfm :text="user.description" :author="user" :i="$i"/> + <Mfm :text="user.description" :author="user"/> </div> <span v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</span> </div> diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue index bcba4196b5..20eb9b3e93 100644 --- a/packages/frontend/src/components/MkUserPopup.vue +++ b/packages/frontend/src/components/MkUserPopup.vue @@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.username"><MkAcct :user="user"/></div> </div> <div :class="$style.description"> - <Mfm v-if="user.description" :class="$style.mfm" :text="user.description" :author="user" :i="$i"/> + <Mfm v-if="user.description" :class="$style.mfm" :text="user.description" :author="user"/> <div v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</div> </div> <div :class="$style.status"> diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue index 746781d71f..4fbaf75454 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.User.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div :class="$style.description"> <div v-if="user.description" :class="$style.mfm"> - <Mfm :text="user.description" :author="user" :i="$i"/> + <Mfm :text="user.description" :author="user"/> </div> <span v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</span> </div> diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index 063b122f8b..1e17bab849 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -5,14 +5,27 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <span v-if="errored">:{{ customEmojiName }}:</span> -<img v-else :class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" :src="url" :alt="alt" :title="alt" decoding="async" @error="errored = true" @load="errored = false"/> +<img + v-else + :class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" + :src="url" + :alt="alt" + :title="alt" + decoding="async" + @error="errored = true" + @load="errored = false" + @click="onClick" +/> </template> <script lang="ts" setup> -import { computed } from 'vue'; +import { computed, inject } from 'vue'; import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js'; import { defaultStore } from '@/store.js'; import { customEmojisMap } from '@/custom-emojis.js'; +import * as os from '@/os.js'; +import copyToClipboard from '@/scripts/copy-to-clipboard.js'; +import { i18n } from '@/i18n.js'; const props = defineProps<{ name: string; @@ -21,8 +34,12 @@ const props = defineProps<{ host?: string | null; url?: string; useOriginalSize?: boolean; + menu?: boolean; + menuReaction?: boolean; }>(); +const react = inject<((name: string) => void) | null>('react', null); + const customEmojiName = computed(() => (props.name[0] === ':' ? props.name.substring(1, props.name.length - 1) : props.name).replace('@.', '')); const isLocal = computed(() => !props.host && (customEmojiName.value.endsWith('@.') || !customEmojiName.value.includes('@'))); @@ -55,6 +72,28 @@ const url = computed(() => { const alt = computed(() => `:${customEmojiName.value}:`); let errored = $ref(url.value == null); + +function onClick(ev: MouseEvent) { + if (props.menu) { + os.popupMenu([{ + type: 'label', + text: `:${props.name}:`, + }, { + text: i18n.ts.copy, + icon: 'ti ti-copy', + action: () => { + copyToClipboard(`:${props.name}:`); + os.success(); + }, + }, ...(props.menuReaction && react ? [{ + text: i18n.ts.doReaction, + icon: 'ti ti-plus', + action: () => { + react(`:${props.name}:`); + }, + }] : [])], ev.currentTarget ?? ev.target); + } +} </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index ab8a342691..d7e1490502 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -33,12 +33,13 @@ type MfmProps = { plain?: boolean; nowrap?: boolean; author?: Misskey.entities.UserLite; - i?: Misskey.entities.UserLite | null; isNote?: boolean; emojiUrls?: string[]; rootScale?: number; nyaize: boolean | 'account'; parsedNodes?: mfm.MfmNode[] | null; + enableEmojiMenu?: boolean; + enableEmojiMenuReaction?: boolean; }; // eslint-disable-next-line import/no-default-export @@ -328,6 +329,8 @@ export default function(props: MfmProps) { normal: props.plain, host: null, useOriginalSize: scale >= 2.5, + menu: props.enableEmojiMenu, + menuReaction: props.enableEmojiMenuReaction, })]; } else { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue index 35021be95e..e0f1a4af90 100644 --- a/packages/frontend/src/components/page/page.text.vue +++ b/packages/frontend/src/components/page/page.text.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps"> - <Mfm :text="block.text" :isNote="false" :i="$i"/> + <Mfm :text="block.text" :isNote="false"/> <MkUrlPreview v-for="url in urls" :key="url" :url="url"/> </div> </template> diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index 911f4e95d2..1d41fe7529 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.bannerFade"></div> </div> <div v-if="channel.description" :class="$style.description"> - <Mfm :text="channel.description" :isNote="false" :i="$i"/> + <Mfm :text="channel.description" :isNote="false"/> </div> </div> diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index 80b94acb6b..4573bbb81c 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="clip" class="_gaps"> <div class="_panel"> <div v-if="clip.description" :class="$style.description"> - <Mfm :text="clip.description" :isNote="false" :i="$i"/> + <Mfm :text="clip.description" :isNote="false"/> </div> <MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton> <MkButton v-else v-tooltip="i18n.ts.favorite" asLike rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 4c425898d5..7ff490bf8b 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div class="description"> <MkOmit> - <Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user" :i="$i"/> + <Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user"/> <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> </MkOmit> </div> @@ -100,7 +100,7 @@ SPDX-License-Identifier: AGPL-3.0-only <Mfm :text="field.name" :plain="true" :colored="false"/> </dt> <dd class="value"> - <Mfm :text="field.value" :author="user" :i="$i" :colored="false"/> + <Mfm :text="field.value" :author="user" :colored="false"/> <i v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" class="ti ti-circle-check" :class="$style.verifiedLink"></i> </dd> </dl> diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue index f2e151468a..8e2192074d 100644 --- a/packages/frontend/src/pages/welcome.timeline.vue +++ b/packages/frontend/src/pages/welcome.timeline.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_panel" :class="$style.content"> <div> <MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> - <Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i"/> + <Mfm v-if="note.text" :text="note.text" :author="note.user"/> <MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA> </div> <div v-if="note.files.length > 0" :class="$style.richcontent"> -- GitLab