diff options
| author | dakkar <dakkar@thenautilus.net> | 2024-06-13 09:06:24 +0100 |
|---|---|---|
| committer | dakkar <dakkar@thenautilus.net> | 2024-06-13 09:06:24 +0100 |
| commit | b2cbd507fe35ad0b31b3b30fdaf180fbfcb1460e (patch) | |
| tree | 725bb756aa65b3e809a49d426d7e01e194777270 | |
| parent | merge: add all missing English translations (!546) (diff) | |
| parent | merge: tweak the rate limit for `notes/create` and other improvements (!548) (diff) | |
| download | sharkey-b2cbd507fe35ad0b31b3b30fdaf180fbfcb1460e.tar.gz sharkey-b2cbd507fe35ad0b31b3b30fdaf180fbfcb1460e.tar.bz2 sharkey-b2cbd507fe35ad0b31b3b30fdaf180fbfcb1460e.zip | |
Merge branch 'develop' into future-2024-05-31
44 files changed, 223 insertions, 79 deletions
diff --git a/locales/en-US.yml b/locales/en-US.yml index 6b637621b0..133df1abbd 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -760,6 +760,10 @@ noCrawleDescription: "Ask search engines to not index your profile page, notes, lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", your notes will be visible to anyone, even if you require followers to be manually approved." alwaysMarkSensitive: "Mark as sensitive by default" loadRawImages: "Load original images instead of showing thumbnails" +searchEngine: "Search Engine For Search MFM" +searchEngineOther: "Other" +searchEngineCustomURIDescription: "The custom URI must be input in the format like \"https://www.google.com/search?q=\\{query}\" or \"https://www.google.com/search?q=%s\"." +searchEngineCusomURI: "Custom URI" disableShowingAnimatedImages: "Don't play animated images" highlightSensitiveMedia: "Highlight sensitive media" verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification." @@ -1880,6 +1884,7 @@ _aboutMisskey: morePatrons: "We also appreciate the support of many other helpers not listed here. Thank you! 🥰" patrons: "Patrons" projectMembers: "Project members" + testers: "Testers" _displayOfSensitiveMedia: respect: "Hide media marked as sensitive" ignore: "Display media marked as sensitive" diff --git a/locales/generateDTS.js b/locales/generateDTS.js index 49807144ec..a175247445 100644 --- a/locales/generateDTS.js +++ b/locales/generateDTS.js @@ -6,7 +6,10 @@ import ts from 'typescript'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const parameterRegExp = /\{(\w+)\}/g; +// braces preceded by backslashes are literal, they don't represent +// parameters; they get cleaned up by `locales/index.js` before +// getting shipped to the browser +const parameterRegExp = /(?<!\\)\{(\w+)\}/g; function createMemberType(item) { if (typeof item !== 'string') { diff --git a/locales/index.d.ts b/locales/index.d.ts index d300ed42a3..28c9fce408 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -3053,6 +3053,22 @@ export interface Locale extends ILocale { */ "loadRawImages": string; /** + * 検索MFMã®æ¤œç´¢ã‚¨ãƒ³ã‚¸ãƒ³ + */ + "searchEngine": string; + /** + * ä»– + */ + "searchEngineOther": string; + /** + * カスタムURI ã¯ã€"https://www.google.com/search?q=\{query}" ã‚„ "https://www.google.com/search?q=%s" ã®ã‚ˆã†ãªå½¢å¼ã§å…¥åŠ›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + */ + "searchEngineCustomURIDescription": string; + /** + * カスタムURI + */ + "searchEngineCusomURI": string; + /** * アニメーション画åƒã‚’å†ç”Ÿã—ãªã„ */ "disableShowingAnimatedImages": string; @@ -7304,6 +7320,10 @@ export interface Locale extends ILocale { * プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼ */ "projectMembers": string; + /** + * テスター + */ + "testers": string; }; "_displayOfSensitiveMedia": { /** diff --git a/locales/index.js b/locales/index.js index 650e552337..c7a693fb77 100644 --- a/locales/index.js +++ b/locales/index.js @@ -49,7 +49,11 @@ const primaries = { }; // ä½•æ•…ã‹æ–‡å—列ã«ãƒãƒƒã‚¯ã‚¹ãƒšãƒ¼ã‚¹æ–‡å—ãŒæ··å…¥ã™ã‚‹ã“ã¨ãŒã‚りã€YAMLãŒå£Šã‚Œã‚‹ã®ã§å–り除ã -const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), ''); +// +// also, we remove the backslashes in front of open braces (the +// backslashes are only needed to tell `generateDTS.js` that the +// braces do not represent parameters) +const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '').replaceAll(new RegExp(/\\+\{/,'g'), '{'); export function build() { const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, import.meta.url), 'utf-8'))) || {}, a), {}); diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4d214b652a..1d4b9f4db4 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -759,6 +759,10 @@ noCrawleDescription: "å¤–éƒ¨ã®æ¤œç´¢ã‚¨ãƒ³ã‚¸ãƒ³ã«ã‚ãªãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒšã lockedAccountInfo: "フォãƒãƒ¼ã‚’承èªåˆ¶ã«ã—ã¦ã‚‚ã€ãƒŽãƒ¼ãƒˆã®å…¬é–‹ç¯„囲を「フォãƒãƒ¯ãƒ¼ã€ã«ã—ãªã„é™ã‚Šã€èª°ã§ã‚‚ã‚ãªãŸã®ãƒŽãƒ¼ãƒˆã‚’見るã“ã¨ãŒã§ãã¾ã™ã€‚" alwaysMarkSensitive: "デフォルトã§ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’センシティブè¨å®šã«ã™ã‚‹" loadRawImages: "添付画åƒã®ã‚µãƒ ãƒã‚¤ãƒ«ã‚’オリジナル画質ã«ã™ã‚‹" +searchEngine: "検索MFMã®æ¤œç´¢ã‚¨ãƒ³ã‚¸ãƒ³" +searchEngineOther: "ä»–" +searchEngineCustomURIDescription: "カスタムURI ã¯ã€\"https://www.google.com/search?q=\\{query}\" ã‚„ \"https://www.google.com/search?q=%s\" ã®ã‚ˆã†ãªå½¢å¼ã§å…¥åŠ›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" +searchEngineCusomURI: "カスタムURI" disableShowingAnimatedImages: "アニメーション画åƒã‚’å†ç”Ÿã—ãªã„" highlightSensitiveMedia: "メディアãŒã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã§ã‚ã‚‹ã“ã¨ã‚’分ã‹ã‚Šã‚„ã™ã表示" verificationEmailSent: "確èªã®ãƒ¡ãƒ¼ãƒ«ã‚’é€ä¿¡ã—ã¾ã—ãŸã€‚メールã«è¨˜è¼‰ã•れãŸãƒªãƒ³ã‚¯ã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ã€è¨å®šã‚’完了ã—ã¦ãã ã•ã„。" @@ -1903,6 +1907,7 @@ _aboutMisskey: morePatrons: "ä»–ã«ã‚‚多ãã®æ–¹ãŒæ”¯æ´ã—ã¦ãれã¦ã„ã¾ã™ã€‚ã‚りãŒã¨ã†ã”ã–ã„ã¾ã™ðŸ¥°" patrons: "支æ´è€…" projectMembers: "プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ¡ãƒ³ãƒãƒ¼" + testers: "テスター" _displayOfSensitiveMedia: respect: "センシティブè¨å®šã•れãŸãƒ¡ãƒ‡ã‚£ã‚¢ã‚’éš ã™" diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 5de6e07d33..625df1feaa 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -132,9 +132,18 @@ export class MfmService { case 'h1': { - text += 'ã€'; + text += '**ã€'; appendChildren(node.childNodes); - text += '】\n'; + text += '】**\n'; + break; + } + + case 'h2': + case 'h3': + { + text += '**'; + appendChildren(node.childNodes); + text += '**\n'; break; } @@ -203,8 +212,6 @@ export class MfmService { } case 'p': - case 'h2': - case 'h3': case 'h4': case 'h5': case 'h6': diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 5d2895cd46..626f03b758 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -30,8 +30,9 @@ export const meta = { prohibitMoved: true, limit: { - duration: ms('1minute'), - max: 5, + duration: ms('1hour'), + max: 300, + minInterval: ms('1sec'), }, kind: 'write:notes', diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 18dfdcdc51..013fa7c37c 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -94,7 +94,7 @@ export async function mainBoot() { }).render(); } } - } + } } catch (error) { // console.error(error); console.error('Failed to initialise the seasonal screen effect canvas context:', error); @@ -236,7 +236,7 @@ export async function mainBoot() { if (Date.now() - lastUsedDate > 1000 * 60 * 60 * 2) { toast(i18n.tsx.welcomeBackWithName({ name: $i.name || $i.username, - })); + }), true); } } miLocalStorage.setItem('lastUsed', Date.now().toString()); diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index 48219d0903..2f44616908 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div class="detail"> <div> - <Mfm :text="report.comment" :linkNavigationBehavior="'window'"/> + <Mfm :text="report.comment" :isBlock="true" :linkNavigationBehavior="'window'"/> </div> <hr/> <div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link" :behavior="'window'">@{{ report.reporter.username }}</MkA></div> diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue index 74d0e7214f..032a815ee6 100644 --- a/packages/frontend/src/components/MkAnnouncementDialog.vue +++ b/packages/frontend/src/components/MkAnnouncementDialog.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only </span> <span :class="$style.title">{{ announcement.title }}</span> </div> - <div :class="$style.text"><Mfm :text="announcement.text"/></div> + <div :class="$style.text"><Mfm :text="announcement.text" :isBlock="true" /></div> <MkButton primary full @click="ok">{{ i18n.ts.ok }}</MkButton> </div> </MkModal> diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 0c9c14ee59..b534ae4c56 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkLoading v-else-if="type === 'waiting'" :class="$style.iconInner" :em="true"/> </div> <header v-if="title" :class="$style.title"><Mfm :text="title"/></header> - <div v-if="text" :class="$style.text"><Mfm :text="text"/></div> + <div v-if="text" :class="$style.text"><Mfm :text="text" :isBlock="true" /></div> <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown"> <template v-if="input.type === 'password'" #prefix><i class="ph-lock ph-bold ph-lg"></i></template> <template #caption> diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue index c92a49d32a..d1809d1073 100644 --- a/packages/frontend/src/components/MkGoogle.vue +++ b/packages/frontend/src/components/MkGoogle.vue @@ -13,6 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import { i18n } from '@/i18n.js'; +import { defaultStore } from '@/store'; const props = defineProps<{ q: string; @@ -21,9 +22,10 @@ const props = defineProps<{ const query = ref(props.q); const search = () => { - const sp = new URLSearchParams(); - sp.append('q', query.value); - window.open(`https://www.google.com/search?${sp.toString()}`, '_blank', 'noopener'); + const searchQuery = encodeURIComponent(query.value); + const searchUrl = defaultStore.state.searchEngine.replace(/{query}|%s\b/g, searchQuery); + + window.open(searchUrl, '_blank', 'noopener'); }; </script> diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 86a44f3188..ba97050154 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> <MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/> - <Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> + <Mfm :text="getNoteSummary(appearNote)" :isBlock="true" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> </div> <article v-else :class="$style.article" @contextmenu.stop="onContextmenu"> <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div> @@ -53,8 +53,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkNoteHeader :note="appearNote" :mini="true" @click.stop/> <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> <div style="container-type: inline-size;"> + <bdi> <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="'respect'"/> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :isBlock="true" :author="appearNote.user" :nyaize="'respect'"/> <MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;" @click.stop/> </p> <div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]"> @@ -71,12 +72,13 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenu="true" :enableEmojiMenuReaction="true" :isAnim="allowAnim" + :isBlock="true" /> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> <div v-else-if="translation"> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :isBlock="true" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> @@ -98,6 +100,7 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </div> <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ph-television ph-bold ph-lg"></i> {{ appearNote.channel.name }}</MkA> + </bdi> </div> <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" :note="appearNote" :maxNumber="16" @click.stop @mockUpdateMyReaction="emitUpdReaction"> <template #more> @@ -339,7 +342,7 @@ let renoting = false; const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - '(q)': () => { if (canRenote.value && !renoted.value && !renoting) { renoting = true; renote(appearNote.value.visibility); } }, + '(q)': () => { if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost); }, 'up|k|shift+tab': focusBefore, 'down|j|tab': focusAfter, 'esc': blur, @@ -439,6 +442,8 @@ if (!props.mock) { } function boostVisibility() { + if (renoting) return; + if (!defaultStore.state.showVisibilitySelectorOnBoost) { renote(defaultStore.state.visibilityOnBoost); } else { @@ -450,6 +455,8 @@ function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); + renoting = true; + if (appearNote.value.channel) { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 991437a510..f0b1ca82a4 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -68,7 +68,7 @@ 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="'respect'"/> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :isBlock="true" :author="appearNote.user" :nyaize="'respect'"/> <MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll"/> </p> <div v-show="appearNote.cw == null || showContent"> @@ -84,13 +84,14 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenu="true" :enableEmojiMenuReaction="true" :isAnim="allowAnim" + :isBlock="true" /> <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-if="translation"> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :isBlock="true" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> @@ -351,7 +352,7 @@ let renoting = false; const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - '(q)': () => { if (canRenote.value && !renoted.value && !renoting) { renoting = true; renote(appearNote.value.visibility); } }, + '(q)': () => { if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost); }, 'esc': blur, 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, @@ -443,6 +444,8 @@ useTooltip(quoteButton, async (showing) => { }); function boostVisibility() { + if (renoting) return; + if (!defaultStore.state.showVisibilitySelectorOnBoost) { renote(defaultStore.state.visibilityOnBoost); } else { @@ -476,6 +479,8 @@ function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); + renoting = true; + if (appearNote.value.channel) { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { diff --git a/packages/frontend/src/components/MkNotePreview.vue b/packages/frontend/src/components/MkNotePreview.vue index 3fcd7593ba..a8853a8a5f 100644 --- a/packages/frontend/src/components/MkNotePreview.vue +++ b/packages/frontend/src/components/MkNotePreview.vue @@ -12,11 +12,11 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div> <p v-if="useCw" :class="$style.cw"> - <Mfm v-if="cw != null && cw != ''" :text="cw" :author="user" :nyaize="'respect'" :i="user" style="margin-right: 8px;"/> + <Mfm v-if="cw != null && cw != ''" :text="cw" :isBlock="true" :author="user" :nyaize="'respect'" :i="user" style="margin-right: 8px;"/> <MkCwButton v-model="showContent" :text="text.trim()" :files="files" :poll="poll" style="margin: 4px 0;"/> </p> <div v-show="!useCw || showContent"> - <Mfm :text="text.trim()" :author="user" :nyaize="'respect'" :i="user"/> + <Mfm :text="text.trim()" :isBlock="true" :author="user" :nyaize="'respect'" :i="user"/> </div> </div> </div> diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 477cf4521a..542e3e79ea 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="'respect'" :emojiUrls="note.emojis"/> + <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :isBlock="true" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll" @click.stop/> </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 37811dd52e..66d1e51a6c 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 :class="$style.content"> <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="'respect'"/> + <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :isBlock="true" :author="note.user" :nyaize="'respect'"/> <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/> </p> <div v-show="note.cw == null || showContent"> diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index e5a58296cc..f849e94e93 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -71,29 +71,29 @@ SPDX-License-Identifier: AGPL-3.0-only <div> <MkA v-if="notification.type === 'reaction' || notification.type === 'reaction:grouped'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> - <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> + <Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/> <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> </MkA> <MkA v-else-if="notification.type === 'renote' || notification.type === 'renote:grouped'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)"> <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> - <Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="true" :author="notification.note.renote?.user"/> + <Mfm :text="getNoteSummary(notification.note.renote)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.renote?.user"/> <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> </MkA> <MkA v-else-if="notification.type === 'reply'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> - <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> + <Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/> </MkA> <MkA v-else-if="notification.type === 'mention'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> - <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> + <Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/> </MkA> <MkA v-else-if="notification.type === 'quote'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> - <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> + <Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/> </MkA> <MkA v-else-if="notification.type === 'note'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> - <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> + <Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/> </MkA> <MkA v-else-if="notification.type === 'pollEnded'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> - <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> + <Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/> <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> </MkA> <div v-else-if="notification.type === 'roleAssigned'" :class="$style.text"> @@ -139,7 +139,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkA v-else-if="notification.type === 'edited'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> - <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> + <Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/> <i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i> </MkA> </div> diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index 7e63bbe82d..8386a783fc 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -9,14 +9,14 @@ 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.deletedNote }})</span> <MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`" @click.stop><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA> - <Mfm v-if="note.text" :text="note.text" :author="note.user" :nyaize="'respect'" :isAnim="allowAnim" :emojiUrls="note.emojis"/> + <Mfm v-if="note.text" :text="note.text" :isBlock="true" :author="note.user" :nyaize="'respect'" :isAnim="allowAnim" :emojiUrls="note.emojis"/> <MkButton v-if="!allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> <MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated && !hideFiles" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton> <div v-if="note.text && translating || note.text && translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> <div v-else> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> + <Mfm :text="translation.text" :isBlock="true" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> </div> </div> <MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`" @click.stop>RN: ...</MkA> diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue index 3082842699..7b9fb3d8ad 100644 --- a/packages/frontend/src/components/MkTextarea.vue +++ b/packages/frontend/src/components/MkTextarea.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.caption"><slot name="caption"></slot></div> <button v-if="mfmPreview" style="font-size: 0.85em;" class="_textButton" type="button" @click="preview = !preview">{{ i18n.ts.preview }}</button> <div v-if="mfmPreview" v-show="preview" v-panel :class="$style.mfmPreview"> - <Mfm :text="v"/> + <Mfm :text="v" :isBlock="true" /> </div> <MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton> diff --git a/packages/frontend/src/components/MkToast.vue b/packages/frontend/src/components/MkToast.vue index a117e49350..f731b3264f 100644 --- a/packages/frontend/src/components/MkToast.vue +++ b/packages/frontend/src/components/MkToast.vue @@ -14,7 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only > <div v-if="showing" class="_acrylic" :class="$style.root" :style="{ zIndex }"> <div style="padding: 16px 24px;"> - {{ message }} + <Mfm v-if="renderMfm" :text="message" plain/> + <template v-else>{{ message }}</template> </div> </div> </Transition> @@ -26,9 +27,12 @@ import { onMounted, ref } from 'vue'; import * as os from '@/os.js'; import { defaultStore } from '@/store.js'; -defineProps<{ +withDefaults(defineProps<{ message: string; -}>(); + renderMfm: boolean; +}>(), { + renderMfm: false, +}); const emit = defineEmits<{ (ev: 'closed'): void; diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue index 63c4af41a0..5658188c41 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"/> + <Mfm :text="user.description" :isBlock="true" :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 18e4bde59d..2aee918114 100644 --- a/packages/frontend/src/components/MkUserPopup.vue +++ b/packages/frontend/src/components/MkUserPopup.vue @@ -28,7 +28,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" :nyaize="false" :class="$style.mfm" :text="user.description" :author="user"/> + <Mfm v-if="user.description" :nyaize="false" :class="$style.mfm" :text="user.description" :isBlock="true" :author="user"/> <div v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</div> </div> <div v-if="user.fields.length > 0" :class="$style.fields"> diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue index a4b9746f4b..efb1ed5593 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"/> + <Mfm :text="user.description" :isBlock="true" :author="user"/> </div> <span v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</span> </div> diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue index d09f4c79ea..7cfa3542c3 100644 --- a/packages/frontend/src/components/SkNote.vue +++ b/packages/frontend/src/components/SkNote.vue @@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> <MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/> - <Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> + <Mfm :text="getNoteSummary(appearNote)" :isBlock="true" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> </div> <article v-else :class="$style.article" @contextmenu.stop="onContextmenu"> <div style="display: flex; padding-bottom: 10px;"> @@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click.stop="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined"> <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="'respect'"/> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :isBlock="true" :author="appearNote.user" :nyaize="'respect'"/> <MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;" @click.stop/> </p> <div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]"> @@ -73,12 +73,13 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenu="true" :enableEmojiMenuReaction="true" :isAnim="allowAnim" + :isBlock="true" /> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> <div v-else-if="translation"> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :isBlock="true" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> @@ -340,7 +341,7 @@ let renoting = false; const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - '(q)': () => { if (canRenote.value && !renoted.value && !renoting) { renoting = true; renote(appearNote.value.visibility); } }, + '(q)': () => { if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost); }, 'up|k|shift+tab': focusBefore, 'down|j|tab': focusAfter, 'esc': blur, @@ -440,6 +441,8 @@ if (!props.mock) { } function boostVisibility() { + if (renoting) return; + if (!defaultStore.state.showVisibilitySelectorOnBoost) { renote(defaultStore.state.visibilityOnBoost); } else { @@ -451,6 +454,8 @@ function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); + renoting = true; + if (appearNote.value.channel) { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue index 56929396f3..8307d4c29d 100644 --- a/packages/frontend/src/components/SkNoteDetailed.vue +++ b/packages/frontend/src/components/SkNoteDetailed.vue @@ -77,7 +77,7 @@ 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="'respect'"/> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :isBlock="true" :author="appearNote.user" :nyaize="'respect'"/> <MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll"/> </p> <div v-show="appearNote.cw == null || showContent"> @@ -92,13 +92,14 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenu="true" :enableEmojiMenuReaction="true" :isAnim="allowAnim" + :isBlock="true" /> <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-if="translation"> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :isBlock="true" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> @@ -360,7 +361,7 @@ let renoting = false; const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - '(q)': () => { if (canRenote.value && !renoted.value && !renoting) { renoting = true; renote(appearNote.value.visibility); } }, + '(q)': () => { if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost); }, 'esc': blur, 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, @@ -452,6 +453,8 @@ useTooltip(quoteButton, async (showing) => { }); function boostVisibility() { + if (renoting) return; + if (!defaultStore.state.showVisibilitySelectorOnBoost) { renote(defaultStore.state.visibilityOnBoost); } else { @@ -485,6 +488,8 @@ function renote(visibility: Visibility, localOnly: boolean = false) { pleaseLogin(); showMovedDialog(); + renoting = true; + if (appearNote.value.channel) { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { diff --git a/packages/frontend/src/components/SkNoteSimple.vue b/packages/frontend/src/components/SkNoteSimple.vue index 533aa60961..b31e337a99 100644 --- a/packages/frontend/src/components/SkNoteSimple.vue +++ b/packages/frontend/src/components/SkNoteSimple.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkNoteHeader :class="$style.header" :classic="true" :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="'respect'" :emojiUrls="note.emojis"/> + <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :isBlock="true" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll" @click.stop/> </p> <div v-show="note.cw == null || showContent"> diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue index 1cffd8dd66..bd17665c2e 100644 --- a/packages/frontend/src/components/SkNoteSub.vue +++ b/packages/frontend/src/components/SkNoteSub.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SkNoteHeader :class="$style.header" :note="note" :classic="true" :mini="true"/> <div :class="$style.content"> <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="'respect'"/> + <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :isBlock="true" :author="note.user" :nyaize="'respect'"/> <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/> </p> <div v-show="note.cw == null || showContent"> diff --git a/packages/frontend/src/components/SkOldNoteWindow.vue b/packages/frontend/src/components/SkOldNoteWindow.vue index bed44bbb08..d2daf3f944 100644 --- a/packages/frontend/src/components/SkOldNoteWindow.vue +++ b/packages/frontend/src/components/SkOldNoteWindow.vue @@ -29,19 +29,19 @@ </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'"/> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :isBlock="true" :author="appearNote.user" :nyaize="'account'"/> <MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll"/> </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="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA> - <Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :nyaize="'account'" :emojiUrls="appearNote.emojis"/> + <Mfm v-if="appearNote.text" :text="appearNote.text" :isBlock="true" :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'" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :isBlock="true" :author="appearNote.user" :nyaize="'account'" :emojiUrls="appearNote.emojis"/> </div> </div> <div v-if="appearNote.files.length > 0"> diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 5fdcaa339b..ac54b60571 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -46,6 +46,7 @@ type MfmProps = { enableEmojiMenuReaction?: boolean; isAnim?: boolean; linkNavigationBehavior?: MkABehavior; + isBlock?: boolean; }; type MfmEvents = { @@ -77,6 +78,8 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven return c.match(/^[0-9a-f]{3,6}$/i) ? c : null; }; + const isBlock = props.isBlock ?? false; + const MkFormula = defineAsyncComponent(() => import('@/components/MkFormula.vue')); /** @@ -393,9 +396,9 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven } case 'center': { - return [h('bdi', h('div', { + return [h('div', { style: 'text-align:center;', - }, genEl(token.children, scale)))]; + }, h('bdi', genEl(token.children, scale)))]; } case 'url': { @@ -431,7 +434,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven } case 'blockCode': { - return [h('bdi', h(MkCode, { + return [h('bdi', { class: 'block' }, h(MkCode, { key: Math.random(), code: token.props.code, lang: token.props.lang ?? undefined, @@ -447,13 +450,13 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven case 'quote': { if (!props.nowrap) { - return [h('bdi', h('div', { + return [h('bdi', { class: 'block' }, h('div', { style: QUOTE_STYLE, - }, genEl(token.children, scale, true)))]; + }, h('bdi',genEl(token.children, scale, true))))]; } else { - return [h('bdi', h('span', { + return [h('span', { style: QUOTE_STYLE, - }, genEl(token.children, scale, true)))]; + }, h('bdi',genEl(token.children, scale, true)))]; } } @@ -504,7 +507,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven } case 'mathBlock': { - return [h('bdi', h(MkFormula, { + return [h('bdi', { class: 'block' }, h(MkFormula, { formula: token.props.formula, block: true, }))]; @@ -518,7 +521,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven } case 'plain': { - return [h('span', genEl(token.children, scale, true))]; + return [h('bdi', h('span', genEl(token.children, scale, true)))]; } default: { @@ -530,7 +533,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven } }).flat(Infinity) as (VNode | string)[]; - return h('bdi', h('span', { + return h('bdi', { ...( isBlock ? { class: 'block' } : {}) }, h('span', { // https://codeday.me/jp/qa/20190424/690106.html style: props.nowrap ? 'white-space: pre; word-wrap: normal; overflow: hidden; text-overflow: ellipsis;' : 'white-space: pre-wrap;', }, genEl(rootAst, props.rootScale ?? 1))); diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue index 2f11c09db2..5f88acb11d 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" :class="$style.textRoot"> - <Mfm :text="block.text ?? ''" :isNote="false"/> + <Mfm :text="block.text ?? ''" :isBlock="true" :isNote="false"/> <div v-if="isEnabledUrlPreview" class="_gaps_s"> <MkUrlPreview v-for="url in urls" :key="url" :url="url"/> </div> diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index c728b014a2..6adf2e590b 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -211,9 +211,10 @@ export function pageWindow(path: string) { }, {}, 'closed'); } -export function toast(message: string) { +export function toast(message: string, renderMfm = false) { popup(MkToast, { message, + renderMfm, }, {}, 'closed'); } diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue index f020e043c6..f5a8fcac42 100644 --- a/packages/frontend/src/pages/about-sharkey.vue +++ b/packages/frontend/src/pages/about-sharkey.vue @@ -105,8 +105,29 @@ SPDX-License-Identifier: AGPL-3.0-only <img src="https://secure.gravatar.com/avatar/ea0ea6451fdb74311efad369bdce018e?s=80&d=identicon" :class="$style.contributorAvatar"> <span :class="$style.contributorUsername">@fEmber</span> </a> + <a href="https://activitypub.software/tess" target="_blank" :class="$style.contributor"> + <img src="https://activitypub.software/uploads/-/system/user/avatar/132/avatar.png?width=128" :class="$style.contributorAvatar"> + <span :class="$style.contributorUsername">@tess</span> + </a> + </div> + <template #description><MkLink url="https://activitypub.software/TransFem-org/Sharkey/-/graphs/develop">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template> + </FormSection> + <FormSection> + <template #label>{{ i18n.ts._aboutMisskey.testers }}</template> + <div :class="$style.contributors" style="margin-bottom: 8px;"> + <a href="https://antani.cyou/@lucent" target="_blank" :class="$style.contributor"> + <img src="https://antani.cyou/proxy/avatar.webp?url=https%3A%2F%2Fantani.cyou%2Ffiles%2Fa2944119-024c-4abd-86e5-64bf0d30b26f&avatar=1" :class="$style.contributorAvatar"> + <span :class="$style.contributorUsername">@lucent</span> + </a> + <a href="https://karilaa.app/@karilaa" target="_blank" :class="$style.contributor"> + <img src="https://karilaa.app/proxy/avatar.webp?url=https%3A%2F%2Fkarilaa.app%2Ffiles%2Fc366e6f9-96d8-4d3b-b996-30e0a7cb3c5a&avatar=1" :class="$style.contributorAvatar"> + <span :class="$style.contributorUsername">@karilaa</span> + </a> + <a href="https://thetransagenda.gay/@phoenix_fairy" target="_blank" :class="$style.contributor"> + <img src="https://thetransagenda.gay/proxy/avatar.webp?url=https%3A%2F%2Fs3.us-east-005.backblazeb2.com%2Ftranssharkey%2Fnull%2Fd93ac6dc-2020-4b5a-bce7-84b41e97a0ac.png&avatar=1" :class="$style.contributorAvatar"> + <span :class="$style.contributorUsername">@phoenix_fairy</span> + </a> </div> - <template #caption><MkLink url="https://activitypub.software/TransFem-org/Sharkey/-/graphs/develop">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template> </FormSection> <FormSection> <template #label>Misskey Contributors</template> diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue index 07bbc46ffc..44dd2e90d8 100644 --- a/packages/frontend/src/pages/announcements.vue +++ b/packages/frontend/src/pages/announcements.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkA :to="`/announcements/${announcement.id}`"><span>{{ announcement.title }}</span></MkA> </div> <div :class="$style.content"> - <Mfm :text="announcement.text"/> + <Mfm :text="announcement.text" :isBlock="true" /> <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> <MkA :to="`/announcements/${announcement.id}`"> <div style="margin-top: 8px; opacity: 0.7; font-size: 85%;"> diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index e49a16fecd..7f49c984a8 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -22,7 +22,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"/> + <Mfm :text="channel.description" :isBlock="true" :isNote="false"/> </div> </div> diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index f89ef036cf..9c42d2d3b8 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_panel"> <div class="_gaps_s" :class="$style.description"> <div v-if="clip.description"> - <Mfm :text="clip.description" :isNote="false"/> + <Mfm :text="clip.description" :isBlock="true" :isNote="false"/> </div> <div v-else>({{ i18n.ts.noDescription }})</div> <div> diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue index 6e61b66a75..febfdbaaf7 100644 --- a/packages/frontend/src/pages/flash/flash.vue +++ b/packages/frontend/src/pages/flash/flash.vue @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else :class="$style.ready"> <div class="_panel main"> <div class="title">{{ flash.title }}</div> - <div class="summary"><Mfm :text="flash.summary"/></div> + <div class="summary"><Mfm :text="flash.summary" :isBlock="true"/></div> <MkButton class="start" gradate rounded large @click="start">Play</MkButton> <div class="info"> <span v-tooltip="i18n.ts.numberOfLikes"><i class="ph-heart ph-bold ph-lg"></i> {{ flash.likedCount }}</span> diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue index 1511928d55..99bd29f802 100644 --- a/packages/frontend/src/pages/gallery/post.vue +++ b/packages/frontend/src/pages/gallery/post.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div class="body"> <div class="title">{{ post.title }}</div> - <div class="description"><Mfm :text="post.description"/></div> + <div class="description"><Mfm :text="post.description" :isBlock="true"/></div> <div class="info"> <i class="ph-clock ph-bold ph-lg"></i> <MkTime :time="post.createdAt" mode="detail"/> </div> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 0269985658..ab4f518d9f 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -67,6 +67,28 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch> <MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch> <MkSwitch v-model="showTickerOnReplies">Show instance ticker on replies</MkSwitch> + <MkSelect v-model="searchEngine" placeholder="Other"> + <template #label>{{ i18n.ts.searchEngine }}</template> + <option + v-for="[key, value] in Object.entries(searchEngineMap)" :key="key" :value="key" + > + {{ value }} + </option> + <!-- If the user is on Other and enters a domain add this one so that the dropdown doesnt go blank --> + <option v-if="useCustomSearchEngine" :value="searchEngine"> + {{ i18n.ts.searchEngineOther }} + </option> + <!-- If one of the other options is selected show this as a blank other --> + <option v-if="!useCustomSearchEngine" value="">{{ i18n.ts.searchEngineOther }}</option> + </MkSelect> + + <div v-if="useCustomSearchEngine"> + <MkInput v-model="searchEngine" :max="300"> + <template #label>{{ i18n.ts.searchEngineCusomURI }}</template> + <template #caption>{{ i18n.ts.searchEngineCustomURIDescription }}</template> + </MkInput> + </div> + <MkRadios v-model="reactionsDisplaySize"> <template #label>{{ i18n.ts.reactionsDisplaySize }}</template> <option value="small">{{ i18n.ts.small }}</option> @@ -279,11 +301,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, ref, watch } from 'vue'; +import { computed, reactive, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkSwitch from '@/components/MkSwitch.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkRadios from '@/components/MkRadios.vue'; +import MkInput from '@/components/MkInput.vue'; import MkRange from '@/components/MkRange.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkButton from '@/components/MkButton.vue'; @@ -292,6 +315,7 @@ import FormLink from '@/components/form/link.vue'; import MkLink from '@/components/MkLink.vue'; import MkInfo from '@/components/MkInfo.vue'; import { langs } from '@/config.js'; +import { searchEngineMap } from '@/scripts/search-engine-map.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; @@ -373,6 +397,9 @@ const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); const showTickerOnReplies = computed(defaultStore.makeGetterSetter('showTickerOnReplies')); +//const searchEngine = computed(defaultStore.makeGetterSetter('searchEngine')); +const searchEngine = computed(defaultStore.makeGetterSetter('searchEngine')); + const noteDesign = computed(defaultStore.makeGetterSetter('noteDesign')); const uncollapseCW = computed(defaultStore.makeGetterSetter('uncollapseCW')); const expandLongNote = computed(defaultStore.makeGetterSetter('expandLongNote')); @@ -563,4 +590,6 @@ definePageMetadata(() => ({ title: i18n.ts.general, icon: 'ph-faders ph-bold ph-lg', })); + +const useCustomSearchEngine = computed(() => !Object.keys(searchEngineMap).includes(searchEngine.value)); </script> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 98c0ab4ce9..095645636d 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -74,7 +74,7 @@ SPDX-License-Identifier: AGPL-3.0-only /> </div> <div class="description"> - <Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user"/> + <Mfm v-if="user.description" :text="user.description" :isBlock="true" :isNote="false" :author="user"/> <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> </div> <div class="fields system"> diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue index be4cb2d2de..4768e31d1d 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="ph-arrow-u-up-left ph-bold ph-lg"></i></MkA> - <Mfm v-if="note.text" :text="note.text" :author="note.user"/> + <Mfm v-if="note.text" :text="note.text" :isBlock="true" :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"> diff --git a/packages/frontend/src/scripts/search-engine-map.ts b/packages/frontend/src/scripts/search-engine-map.ts new file mode 100644 index 0000000000..3fb06d135d --- /dev/null +++ b/packages/frontend/src/scripts/search-engine-map.ts @@ -0,0 +1,12 @@ +//store the URL and if its none of these its a custom one +export const searchEngineMap = { + //The first one is the default search engine + 'https://www.google.com/search?q={query}': 'Google', + 'https://duckduckgo.com/?q={query}': 'Duckduckgo', + 'https://www.bing.com/search?q={query}': 'Bing', + 'https://search.yahoo.com/search?p={query}': 'Yahoo', + 'https://www.ecosia.org/search?q={query}': 'Ecosia', + 'https://www.qwant.com/?q={query}': 'Qwant', + 'https://search.aol.com/aol/search?q={query}': 'AOL', + 'https://yandex.com/search?text={query}': 'Yandex', +}; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index f3f6746b62..e975439456 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -6,6 +6,7 @@ import { markRaw, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { miLocalStorage } from './local-storage.js'; +import { searchEngineMap } from './scripts/search-engine-map.js'; import type { SoundType } from '@/scripts/sound.js'; import { Storage } from '@/pizzax.js'; import { hemisphere } from '@/scripts/intl-const.js'; @@ -311,6 +312,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + searchEngine: { + where: 'account', + default: Object.keys(searchEngineMap)[0], + }, noteDesign: { where: 'device', default: 'sharkey' as 'sharkey' | 'misskey', diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index f77739fdbc..37d1a3c557 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -565,7 +565,7 @@ html[data-color-mode=dark] ._woodenFrame { // MFM ----------------------------- -div > bdi, p > bdi { display: block } +bdi.block { display: block } ._mfm_blur_ { filter: blur(6px); @@ -716,4 +716,4 @@ div > bdi, p > bdi { display: block } 100% { opacity: 1; } -}
\ No newline at end of file +} |