From 4d024d116509564d7a7424aaee773c9256529b87 Mon Sep 17 00:00:00 2001
From: Hazelnoot <acomputerdog@gmail.com>
Date: Wed, 4 Jun 2025 14:51:49 -0400
Subject: use responsive gaps between note toolbar items

---
 packages/frontend/src/components/SkOldNoteWindow.vue | 20 +-------------------
 1 file changed, 1 insertion(+), 19 deletions(-)

(limited to 'packages/frontend/src/components/SkOldNoteWindow.vue')

diff --git a/packages/frontend/src/components/SkOldNoteWindow.vue b/packages/frontend/src/components/SkOldNoteWindow.vue
index b6dbec81c5..d4a4dec1cb 100644
--- a/packages/frontend/src/components/SkOldNoteWindow.vue
+++ b/packages/frontend/src/components/SkOldNoteWindow.vue
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</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>
 			</div>
-			<footer :class="$style.footer">
+			<footer :class="$style.footer" class="_gaps _h_gaps">
 				<div :class="$style.noteFooterInfo">
 					<MkTime :time="appearNote.createdAt" mode="detail"/>
 				</div>
@@ -280,23 +280,11 @@ const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceT
 	padding: 8px;
 	opacity: 0.7;
 
-	&:not(:last-child) {
-		margin-right: 1.5em;
-	}
-
 	&:hover {
 		color: var(--MI_THEME-fgHighlighted);
 	}
 }
 
-@container (max-width: 350px) {
-	.noteFooterButton {
-		&:not(:last-child) {
-			margin-right: 0.1em;
-		}
-	}
-}
-
 @container (max-width: 500px) {
 	.root {
 		font-size: 0.9em;
@@ -323,11 +311,5 @@ const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceT
 		width: 50px;
 		height: 50px;
 	}
-
-	.noteFooterButton {
-		&:not(:last-child) {
-			margin-right: 0.1em;
-		}
-	}
 }
 </style>
-- 
cgit v1.2.3-freya


From 25174e837affc0b4eaaa2fe3a68b940bc498027d Mon Sep 17 00:00:00 2001
From: Hazelnoot <acomputerdog@gmail.com>
Date: Wed, 4 Jun 2025 15:03:01 -0400
Subject: allow note toolbar to scroll

https://developer.mozilla.org/en-US/docs/Web/CSS/overflow#accessibility
---
 locales/index.d.ts                                   |  4 ++++
 packages/frontend/src/components/MkNote.vue          |  6 +++---
 packages/frontend/src/components/MkNoteDetailed.vue  |  6 ++----
 packages/frontend/src/components/MkNoteSub.vue       |  6 ++----
 packages/frontend/src/components/SkNote.vue          |  6 +++---
 packages/frontend/src/components/SkNoteDetailed.vue  | 16 ++++++++--------
 packages/frontend/src/components/SkNoteSub.vue       |  6 +++---
 packages/frontend/src/components/SkOldNoteWindow.vue | 13 +++++++------
 sharkey-locales/en-US.yml                            |  2 ++
 9 files changed, 34 insertions(+), 31 deletions(-)

(limited to 'packages/frontend/src/components/SkOldNoteWindow.vue')

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 182cf03284..6217a08425 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -13233,6 +13233,10 @@ export interface Locale extends ILocale {
      * Enabled (copy CW and prepend "RE:")
      */
     "keepCwPrependRe": string;
+    /**
+     * Note controls
+     */
+    "noteFooterLabel": string;
 }
 declare const locales: {
     [lang: string]: Locale;
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 862dccd0d3..16aed7898e 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -113,7 +113,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkA :to="`/notes/${appearNote.id}/reactions`" :class="[$style.reactionOmitted]">{{ i18n.ts.more }}</MkA>
 				</template>
 			</MkReactionsViewer>
-			<footer :class="$style.footer" class="_gaps _h_gaps">
+			<footer :class="$style.footer" class="_gaps _h_gaps" tabindex="0" role="group" :aria-label="i18n.ts.noteFooterLabel">
 				<button :class="$style.footerButton" class="_button" @click.stop @click="reply()">
 					<i class="ti ti-arrow-back-up"></i>
 					<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p>
@@ -913,11 +913,11 @@ function emitUpdReaction(emoji: string, delta: number) {
 	.footer {
 		display: flex;
 		align-items: center;
-		justify-content: space-between;
+		justify-content: flex-start;
 		position: relative;
 		z-index: 1;
 		margin-top: 0.4em;
-		max-width: 400px;
+		overflow-x: auto;
 	}
 
 	&:hover > .article > .main > .footer > .footerButton {
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 4a8c51402c..be0676dc5f 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -118,7 +118,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 			<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
 		</div>
-		<footer :class="$style.footer" class="_gaps _h_gaps">
+		<footer :class="$style.footer" class="_gaps _h_gaps" tabindex="0" role="group" :aria-label="i18n.ts.noteFooterLabel">
 			<div :class="$style.noteFooterInfo">
 				<div v-if="appearNote.updatedAt">
 					{{ i18n.ts.edited }}: <MkTime :time="appearNote.updatedAt" mode="detail"/>
@@ -889,9 +889,7 @@ function animatedMFM() {
 	position: relative;
 	z-index: 1;
 	margin-top: 0.4em;
-	width: max-content;
-	min-width: min-content;
-	max-width: fit-content;
+	overflow-x: auto;
 }
 
 .replyTo {
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 09c3ed67fa..6b70fddecf 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkSubNoteContent :class="$style.text" :note="note" :translating="translating" :translation="translation" :expandAllCws="props.expandAllCws"/>
 				</div>
 			</div>
-			<footer :class="$style.footer" class="_gaps _h_gaps">
+			<footer :class="$style.footer" class="_gaps _h_gaps" tabindex="0" role="group" :aria-label="i18n.ts.noteFooterLabel">
 				<MkReactionsViewer ref="reactionsViewer" :note="note"/>
 				<button class="_button" :class="$style.noteFooterButton" @click="reply()">
 					<i class="ph-arrow-u-up-left ph-bold ph-lg"></i>
@@ -422,9 +422,7 @@ if (props.detail) {
 	position: relative;
 	z-index: 1;
 	margin-top: 0.4em;
-	width: max-content;
-	min-width: min-content;
-	max-width: fit-content;
+	overflow-x: auto;
 }
 
 .main {
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index a3645a6a64..42b2e2a957 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkA :to="`/notes/${appearNote.id}/reactions`" :class="[$style.reactionOmitted]">{{ i18n.ts.more }}</MkA>
 				</template>
 			</MkReactionsViewer>
-			<footer :class="$style.footer" class="_gaps _h_gaps">
+			<footer :class="$style.footer" class="_gaps _h_gaps" tabindex="0" role="group" :aria-label="i18n.ts.noteFooterLabel">
 				<button :class="$style.footerButton" class="_button" @click.stop @click="reply()">
 					<i class="ti ti-arrow-back-up"></i>
 					<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p>
@@ -921,11 +921,11 @@ function emitUpdReaction(emoji: string, delta: number) {
 	.footer {
 		display: flex;
 		align-items: center;
-		justify-content: space-between;
+		justify-content: flex-start;
 		position: relative;
 		z-index: 1;
 		margin-top: 0.4em;
-		max-width: 400px;
+		overflow-x: auto;
 	}
 
 	&:hover > .article > .main > .footer > .footerButton {
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index 9501b909bb..d57a90628e 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -132,7 +132,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</MkA>
 		</div>
 		<MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" style="margin-top: 6px;" :note="appearNote"/>
-		<footer :class="$style.footer" class="_gaps _h_gaps">
+		<footer :class="$style.footer" class="_gaps _h_gaps" tabindex="0" role="group" :aria-label="i18n.ts.noteFooterLabel">
 			<button class="_button" :class="$style.noteFooterButton" @click="reply()">
 				<i class="ti ti-arrow-back-up"></i>
 				<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
@@ -918,13 +918,13 @@ onUnmounted(() => {
 }
 
 .footer {
-		display: flex;
-		align-items: center;
-		justify-content: space-between;
-		position: relative;
-		z-index: 1;
-		margin-top: 0.4em;
-		max-width: 400px;
+	display: flex;
+	align-items: center;
+	justify-content: flex-start;
+	position: relative;
+	z-index: 1;
+	margin-top: 0.4em;
+	overflow-x: auto;
 }
 
 .replyTo {
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index 5f371f8bd7..a520c9744e 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</div>
 			<MkReactionsViewer ref="reactionsViewer" :note="note"/>
-			<footer :class="$style.footer" class="_gaps _h_gaps">
+			<footer :class="$style.footer" class="_gaps _h_gaps" tabindex="0" role="group" :aria-label="i18n.ts.noteFooterLabel">
 				<button class="_button" :class="$style.noteFooterButton" @click="reply()">
 					<i class="ph-arrow-u-up-left ph-bold ph-lg"></i>
 					<p v-if="note.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ note.repliesCount }}</p>
@@ -449,11 +449,11 @@ if (props.detail) {
 .footer {
 	display: flex;
 	align-items: center;
-	justify-content: space-between;
+	justify-content: flex-start;
 	position: relative;
 	z-index: 1;
 	margin-top: 0.4em;
-	max-width: 400px;
+	overflow-x: auto;
 }
 
 .main {
diff --git a/packages/frontend/src/components/SkOldNoteWindow.vue b/packages/frontend/src/components/SkOldNoteWindow.vue
index d4a4dec1cb..778a776167 100644
--- a/packages/frontend/src/components/SkOldNoteWindow.vue
+++ b/packages/frontend/src/components/SkOldNoteWindow.vue
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</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>
 			</div>
-			<footer :class="$style.footer" class="_gaps _h_gaps">
+			<footer :class="$style.footer" class="_gaps _h_gaps" tabindex="0" role="group" :aria-label="i18n.ts.noteFooterLabel">
 				<div :class="$style.noteFooterInfo">
 					<MkTime :time="appearNote.createdAt" mode="detail"/>
 				</div>
@@ -163,11 +163,12 @@ const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceT
 }
 
 .footer {
-		position: relative;
-		z-index: 1;
-		margin-top: 0.4em;
-		width: max-content;
-		min-width: max-content;
+	position: relative;
+	z-index: 1;
+	margin-top: 0.4em;
+	width: max-content;
+	min-width: max-content;
+	overflow-x: auto;
 }
 
 .note {
diff --git a/sharkey-locales/en-US.yml b/sharkey-locales/en-US.yml
index f9463f8221..bb9471a7ef 100644
--- a/sharkey-locales/en-US.yml
+++ b/sharkey-locales/en-US.yml
@@ -619,3 +619,5 @@ keepCwDescription: "When replying to a post with a Content Warning, automaticall
 keepCwDisabled: "Disabled (do not copy CWs)"
 keepCwEnabled: "Enabled (copy CWs verbatim)"
 keepCwPrependRe: "Enabled (copy CW and prepend \"RE:\")"
+
+noteFooterLabel: "Note controls"
-- 
cgit v1.2.3-freya


From 3d804c560d402842fc1c481f63bd525aa3b0625f Mon Sep 17 00:00:00 2001
From: Hazelnoot <acomputerdog@gmail.com>
Date: Sat, 7 Jun 2025 20:54:21 -0400
Subject: use SKUrlPreviewGroup in more places

---
 packages/frontend/src/components/MkNote.vue          |  7 ++++---
 packages/frontend/src/components/MkNoteDetailed.vue  |  7 +++----
 packages/frontend/src/components/SkNote.vue          |  7 ++++---
 packages/frontend/src/components/SkNoteDetailed.vue  |  7 +++----
 packages/frontend/src/components/SkOldNoteWindow.vue | 10 ++++------
 packages/frontend/src/components/page/page.text.vue  | 15 +++------------
 packages/frontend/src/pages/chat/XMessage.vue        |  8 ++++----
 7 files changed, 25 insertions(+), 36 deletions(-)

(limited to 'packages/frontend/src/components/SkOldNoteWindow.vue')

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 16aed7898e..37882ef5b9 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -95,7 +95,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</div>
 					<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :local="!appearNote.user.host" :author="appearNote.user" :emojiUrls="appearNote.emojis" :class="$style.poll" @click.stop/>
 					<div v-if="isEnabledUrlPreview">
-						<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" :class="$style.urlPreview" @click.stop/>
+						<SkUrlPreviewGroup :sourceUrls="urls" :sourceNote="appearNote" :compact="true" :detail="false" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" :class="$style.urlPreview" @click.stop/>
 					</div>
 					<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
 					<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click.stop @click="collapsed = false">
@@ -237,6 +237,7 @@ import SkMutedNote from '@/components/SkMutedNote.vue';
 import SkNoteTranslation from '@/components/SkNoteTranslation.vue';
 import { getSelfNoteIds } from '@/utility/get-self-note-ids.js';
 import { extractPreviewUrls } from '@/utility/extract-preview-urls.js';
+import SkUrlPreviewGroup from '@/components/SkUrlPreviewGroup.vue';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -304,9 +305,9 @@ const galleryEl = useTemplateRef('galleryEl');
 const isMyRenote = $i && ($i.id === note.value.userId);
 const showContent = ref(prefer.s.uncollapseCW);
 const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
-const urls = computed(() => parsed.value ? extractPreviewUrls(props.note, parsed.value) : null);
+const urls = computed(() => parsed.value ? extractPreviewUrls(props.note, parsed.value) : []);
 const selfNoteIds = computed(() => getSelfNoteIds(props.note));
-const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
+const isLong = shouldCollapsed(appearNote.value, urls.value);
 const collapsed = ref(prefer.s.expandLongNote && appearNote.value.cw == null && isLong ? false : appearNote.value.cw == null && isLong);
 const isDeleted = ref(false);
 const renoted = ref(false);
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index be0676dc5f..3f692d4964 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -112,7 +112,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 				<MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :local="!appearNote.user.host" :class="$style.poll" :author="appearNote.user" :emojiUrls="appearNote.emojis"/>
 				<div v-if="isEnabledUrlPreview">
-					<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" style="margin-top: 6px;"/>
+					<SkUrlPreviewGroup :sourceNodes="nodes" :sourceNote="appearNote" :compact="true" :detail="true" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" style="margin-top: 6px;" @click.stop/>
 				</div>
 				<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote" :expandAllCws="props.expandAllCws"/></div>
 			</div>
@@ -286,7 +286,7 @@ import { DI } from '@/di.js';
 import SkMutedNote from '@/components/SkMutedNote.vue';
 import SkNoteTranslation from '@/components/SkNoteTranslation.vue';
 import { getSelfNoteIds } from '@/utility/get-self-note-ids.js';
-import { extractPreviewUrls } from '@/utility/extract-preview-urls.js';
+import SkUrlPreviewGroup from '@/components/SkUrlPreviewGroup.vue';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -339,8 +339,7 @@ const isDeleted = ref(false);
 const renoted = ref(false);
 const translation = ref<Misskey.entities.NotesTranslateResponse | false | null>(null);
 const translating = ref(false);
-const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
-const urls = computed(() => parsed.value ? extractPreviewUrls(props.note, parsed.value) : null);
+const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : []);
 const selfNoteIds = computed(() => getSelfNoteIds(props.note));
 const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
 const allowAnim = ref(prefer.s.advancedMfm && prefer.s.animatedMfm);
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index 42b2e2a957..24293343ac 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</div>
 					<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :local="!appearNote.user.host" :author="appearNote.user" :emojiUrls="appearNote.emojis" :class="$style.poll" @click.stop/>
 					<div v-if="isEnabledUrlPreview">
-						<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" :class="$style.urlPreview" @click.stop/>
+						<SkUrlPreviewGroup :sourceUrls="urls" :sourceNote="appearNote" :compact="true" :detail="false" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" :class="$style.urlPreview" @click.stop/>
 					</div>
 					<div v-if="appearNote.renote" :class="$style.quote"><SkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
 					<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click.stop @click="collapsed = false">
@@ -237,6 +237,7 @@ import SkMutedNote from '@/components/SkMutedNote.vue';
 import SkNoteTranslation from '@/components/SkNoteTranslation.vue';
 import { getSelfNoteIds } from '@/utility/get-self-note-ids.js';
 import { extractPreviewUrls } from '@/utility/extract-preview-urls.js';
+import SkUrlPreviewGroup from '@/components/SkUrlPreviewGroup.vue';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -304,9 +305,9 @@ const galleryEl = useTemplateRef('galleryEl');
 const isMyRenote = $i && ($i.id === note.value.userId);
 const showContent = ref(prefer.s.uncollapseCW);
 const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
-const urls = computed(() => parsed.value ? extractPreviewUrls(props.note, parsed.value) : null);
+const urls = computed(() => parsed.value ? extractPreviewUrls(props.note, parsed.value) : []);
 const selfNoteIds = computed(() => getSelfNoteIds(props.note));
-const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
+const isLong = shouldCollapsed(appearNote.value, urls.value);
 const collapsed = ref(prefer.s.expandLongNote && appearNote.value.cw == null && isLong ? false : appearNote.value.cw == null && isLong);
 const isDeleted = ref(false);
 const renoted = ref(false);
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index d57a90628e..3c867a9559 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -117,7 +117,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 				<MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :local="!appearNote.user.host" :class="$style.poll" :author="appearNote.user" :emojiUrls="appearNote.emojis"/>
 				<div v-if="isEnabledUrlPreview">
-					<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" style="margin-top: 6px;"/>
+					<SkUrlPreviewGroup :sourceNodes="nodes" :sourceNote="appearNote" :compact="true" :detail="true" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" style="margin-top: 6px;" @click.stop/>
 				</div>
 				<div v-if="appearNote.renote" :class="$style.quote"><SkNoteSimple :note="appearNote.renote" :class="$style.quoteNote" :expandAllCws="props.expandAllCws"/></div>
 			</div>
@@ -291,7 +291,7 @@ import { DI } from '@/di.js';
 import SkMutedNote from '@/components/SkMutedNote.vue';
 import SkNoteTranslation from '@/components/SkNoteTranslation.vue';
 import { getSelfNoteIds } from '@/utility/get-self-note-ids.js';
-import { extractPreviewUrls } from '@/utility/extract-preview-urls';
+import SkUrlPreviewGroup from '@/components/SkUrlPreviewGroup.vue';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -345,8 +345,7 @@ const isDeleted = ref(false);
 const renoted = ref(false);
 const translation = ref<Misskey.entities.NotesTranslateResponse | false | null>(null);
 const translating = ref(false);
-const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
-const urls = computed(() => parsed.value ? extractPreviewUrls(props.note, parsed.value) : null);
+const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : []);
 const selfNoteIds = computed(() => getSelfNoteIds(props.note));
 const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null);
 const allowAnim = ref(prefer.s.advancedMfm && prefer.s.animatedMfm ? true : false);
diff --git a/packages/frontend/src/components/SkOldNoteWindow.vue b/packages/frontend/src/components/SkOldNoteWindow.vue
index 778a776167..39ad2bd7c2 100644
--- a/packages/frontend/src/components/SkOldNoteWindow.vue
+++ b/packages/frontend/src/components/SkOldNoteWindow.vue
@@ -40,14 +40,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<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" :isBlock="true" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
+					<Mfm v-if="appearNote.text" :text="appearNote.text" :parsedNodes="parsed" :isBlock="true" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
 					<a v-if="appearNote.renote != null" :class="$style.rn">RN:</a>
 					<SkNoteTranslation :note="note" :translation="translation" :translating="translating"></SkNoteTranslation>
 					<div v-if="appearNote.files && appearNote.files.length > 0">
 						<MkMediaList :mediaList="appearNote.files"/>
 					</div>
 					<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :local="!appearNote.user.host" :author="appearNote.user" :emojiUrls="appearNote.emojis" :class="$style.poll"/>
-					<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" style="margin-top: 6px;"/>
+					<SkUrlPreviewGroup :sourceNodes="parsed" :sourceNote="appearNote" :compact="true" :detail="true" :showAsQuote="!appearNote.user.rejectQuotes" :skipNoteIds="selfNoteIds" style="margin-top: 6px;" @click.stop/>
 					<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
 				</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>
@@ -83,7 +83,6 @@ import MkMediaList from '@/components/MkMediaList.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
 import MkWindow from '@/components/MkWindow.vue';
 import MkPoll from '@/components/MkPoll.vue';
-import MkUrlPreview from '@/components/MkUrlPreview.vue';
 import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
 import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
@@ -93,7 +92,7 @@ import { prefer } from '@/preferences';
 import { getPluginHandlers } from '@/plugin.js';
 import SkNoteTranslation from '@/components/SkNoteTranslation.vue';
 import { getSelfNoteIds } from '@/utility/get-self-note-ids';
-import { extractPreviewUrls } from '@/utility/extract-preview-urls.js';
+import SkUrlPreviewGroup from '@/components/SkUrlPreviewGroup.vue';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
@@ -143,12 +142,11 @@ const isRenote = (
 
 const el = shallowRef<HTMLElement>();
 const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
-const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
+const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : []);
 
 const showContent = ref(false);
 const translation = ref<Misskey.entities.NotesTranslateResponse | false | null>(null);
 const translating = ref(false);
-const urls = computed(() => parsed.value ? extractPreviewUrls(props.note, parsed.value) : null);
 const selfNoteIds = computed(() => getSelfNoteIds(props.note));
 const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceTicker === 'remote' && appearNote.value.user.instance);
 
diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue
index ef3524fe7a..3891380dd0 100644
--- a/packages/frontend/src/components/page/page.text.vue
+++ b/packages/frontend/src/components/page/page.text.vue
@@ -7,29 +7,20 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div class="_gaps" :class="$style.textRoot">
 	<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" :showAsQuote="!page.user.rejectQuotes"/>
+		<SkUrlPreviewGroup :sourceText="block.text" :showAsQuote="!page.user.rejectQuotes" @click.stop/>
 	</div>
 </div>
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, computed } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
 import * as Misskey from 'misskey-js';
-import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
 import { isEnabledUrlPreview } from '@/instance.js';
+import SkUrlPreviewGroup from '@/components/SkUrlPreviewGroup.vue';
 
-const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
-
-const props = defineProps<{
+defineProps<{
 	block: Misskey.entities.PageBlock,
 	page: Misskey.entities.Page,
 }>();
-
-const urls = computed(() => {
-	if (!props.block.text) return [];
-	return extractUrlFromMfm(mfm.parse(props.block.text));
-});
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/chat/XMessage.vue b/packages/frontend/src/pages/chat/XMessage.vue
index bc30c69f8d..1536a91196 100644
--- a/packages/frontend/src/pages/chat/XMessage.vue
+++ b/packages/frontend/src/pages/chat/XMessage.vue
@@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				ref="text"
 				class="_selectable"
 				:text="message.text"
+				:parsedNotes="parsed"
 				:i="$i"
 				:nyaize="'respect'"
 				:enableEmojiMenu="true"
@@ -21,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			/>
 			<MkMediaList v-if="message.file" :mediaList="[message.file]" :class="$style.file"/>
 		</MkFukidashi>
-		<MkUrlPreview v-for="url in urls" :key="url" :url="url" :showAsQuote="!message.fromUser.rejectQuotes" style="margin: 8px 0;"/>
+		<SkUrlPreviewGroup :sourceNodes="parsed" :showAsQuote="!message.fromUser.rejectQuotes" style="margin: 8px 0;"/>
 		<div :class="$style.footer">
 			<button class="_textButton" style="color: currentColor;" @click="showMenu"><i class="ti ti-dots-circle-horizontal"></i></button>
 			<MkTime :class="$style.time" :time="message.createdAt"/>
@@ -58,8 +59,6 @@ import { url } from '@@/js/config.js';
 import { isLink } from '@@/js/is-link.js';
 import type { MenuItem } from '@/types/menu.js';
 import type { NormalizedChatMessage } from './room.vue';
-import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
-import MkUrlPreview from '@/components/MkUrlPreview.vue';
 import { ensureSignin } from '@/i.js';
 import { misskeyApi } from '@/utility/misskey-api.js';
 import { i18n } from '@/i18n.js';
@@ -74,6 +73,7 @@ import { prefer } from '@/preferences.js';
 import { DI } from '@/di.js';
 import { getHTMLElementOrNull } from '@/utility/get-dom-node-or-null.js';
 import SkTransitionGroup from '@/components/SkTransitionGroup.vue';
+import SkUrlPreviewGroup from '@/components/SkUrlPreviewGroup.vue';
 
 const $i = ensureSignin();
 
@@ -83,7 +83,7 @@ const props = defineProps<{
 }>();
 
 const isMe = computed(() => props.message.fromUserId === $i.id);
-const urls = computed(() => props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : []);
+const parsed = computed(() => props.message.text ? mfm.parse(props.message.text) : []);
 
 provide(DI.mfmEmojiReactCallback, (reaction) => {
 	if ($i.policies.chatAvailability !== 'available') return;
-- 
cgit v1.2.3-freya


From 7eca06d672604fe03ce10591263bd04cf58e54c6 Mon Sep 17 00:00:00 2001
From: Hazelnoot <acomputerdog@gmail.com>
Date: Fri, 23 May 2025 19:48:55 -0400
Subject: use pnpm package alias for @transfem-org/sfm-js

---
 packages/backend/package.json                                  | 2 +-
 packages/backend/src/core/MfmService.ts                        | 2 +-
 packages/backend/src/core/NoteCreateService.ts                 | 2 +-
 packages/backend/src/core/NoteEditService.ts                   | 2 +-
 packages/backend/src/core/activitypub/ApMfmService.ts          | 2 +-
 packages/backend/src/core/activitypub/ApRendererService.ts     | 2 +-
 packages/backend/src/misc/extract-custom-emojis-from-mfm.ts    | 2 +-
 packages/backend/src/misc/extract-hashtags.ts                  | 2 +-
 packages/backend/src/misc/extract-mentions.ts                  | 2 +-
 packages/backend/src/server/api/endpoints/i/update.ts          | 2 +-
 packages/backend/src/server/api/mastodon/MastodonConverters.ts | 2 +-
 packages/backend/src/server/web/FeedService.ts                 | 2 +-
 packages/backend/test/unit/MfmService.ts                       | 2 +-
 packages/backend/test/unit/extract-mentions.ts                 | 2 +-
 packages/frontend-embed/package.json                           | 4 ++--
 packages/frontend-embed/src/components/EmMfm.ts                | 2 +-
 packages/frontend-embed/src/components/EmNote.vue              | 2 +-
 packages/frontend-embed/src/components/EmNoteDetailed.vue      | 2 +-
 packages/frontend/package.json                                 | 2 +-
 packages/frontend/src/components/MkNote.vue                    | 2 +-
 packages/frontend/src/components/MkNoteDetailed.vue            | 2 +-
 packages/frontend/src/components/MkPostForm.vue                | 2 +-
 packages/frontend/src/components/MkSubNoteContent.vue          | 2 +-
 packages/frontend/src/components/SkNote.vue                    | 2 +-
 packages/frontend/src/components/SkNoteDetailed.vue            | 2 +-
 packages/frontend/src/components/SkOldNoteWindow.vue           | 2 +-
 packages/frontend/src/components/global/MkMfm.ts               | 2 +-
 packages/frontend/src/pages/chat/XMessage.vue                  | 2 +-
 packages/frontend/src/utility/check-animated-mfm.ts            | 2 +-
 packages/frontend/src/utility/extract-mentions.ts              | 2 +-
 packages/frontend/src/utility/extract-preview-urls.ts          | 2 +-
 packages/frontend/src/utility/extract-url-from-mfm.ts          | 2 +-
 32 files changed, 33 insertions(+), 33 deletions(-)

(limited to 'packages/frontend/src/components/SkOldNoteWindow.vue')

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 69d38f3bfb..70432d1bd1 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -92,7 +92,7 @@
 		"@smithy/node-http-handler": "2.5.0",
 		"@swc/cli": "0.7.3",
 		"@swc/core": "1.11.24",
-		"@transfem-org/sfm-js": "0.24.6",
+		"mfm-js": "npm:@transfem-org/sfm-js@0.24.6",
 		"@twemoji/parser": "15.1.1",
 		"accepts": "1.3.8",
 		"ajv": "8.17.1",
diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index d85ac7c807..551b25394a 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -15,7 +15,7 @@ import { intersperse } from '@/misc/prelude/array.js';
 import { normalizeForSearch } from '@/misc/normalize-for-search.js';
 import type { IMentionedRemoteUsers } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
-import type * as mfm from '@transfem-org/sfm-js';
+import type * as mfm from 'mfm-js';
 
 const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
 const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/;
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index a9f4083446..f8584a4a48 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -4,7 +4,7 @@
  */
 
 import { setImmediate } from 'node:timers/promises';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { In, DataSource, IsNull, LessThan } from 'typeorm';
 import * as Redis from 'ioredis';
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts
index a359381573..d963bf1945 100644
--- a/packages/backend/src/core/NoteEditService.ts
+++ b/packages/backend/src/core/NoteEditService.ts
@@ -4,7 +4,7 @@
  */
 
 import { setImmediate } from 'node:timers/promises';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { DataSource, In, IsNull, LessThan } from 'typeorm';
 import * as Redis from 'ioredis';
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts
index c4a948429a..ddb6461746 100644
--- a/packages/backend/src/core/activitypub/ApMfmService.ts
+++ b/packages/backend/src/core/activitypub/ApMfmService.ts
@@ -4,7 +4,7 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { MfmService, Appender } from '@/core/MfmService.js';
 import type { MiNote } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 789611fd97..08a8f30049 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -6,7 +6,7 @@
 import { createPublicKey, randomUUID } from 'node:crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import { In } from 'typeorm';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { UnrecoverableError } from 'bullmq';
 import { Element, Text } from 'domhandler';
 import { DI } from '@/di-symbols.js';
diff --git a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts
index 36a9b8e1f4..73ae9abb54 100644
--- a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts
+++ b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { unique } from '@/misc/prelude/array.js';
 
 export function extractCustomEmojisFromMfm(nodes: mfm.MfmNode[]): string[] {
diff --git a/packages/backend/src/misc/extract-hashtags.ts b/packages/backend/src/misc/extract-hashtags.ts
index ed7606d995..d3d245d414 100644
--- a/packages/backend/src/misc/extract-hashtags.ts
+++ b/packages/backend/src/misc/extract-hashtags.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { unique } from '@/misc/prelude/array.js';
 
 export function extractHashtags(nodes: mfm.MfmNode[]): string[] {
diff --git a/packages/backend/src/misc/extract-mentions.ts b/packages/backend/src/misc/extract-mentions.ts
index bb21c32ffb..2ec9349718 100644
--- a/packages/backend/src/misc/extract-mentions.ts
+++ b/packages/backend/src/misc/extract-mentions.ts
@@ -5,7 +5,7 @@
 
 // test is located in test/extract-mentions
 
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 
 export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] {
 	// TODO: 重複を削除
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index e632915f62..5767880531 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { Inject, Injectable } from '@nestjs/common';
 import ms from 'ms';
 import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
diff --git a/packages/backend/src/server/api/mastodon/MastodonConverters.ts b/packages/backend/src/server/api/mastodon/MastodonConverters.ts
index 02ce31c4f8..df8d68042a 100644
--- a/packages/backend/src/server/api/mastodon/MastodonConverters.ts
+++ b/packages/backend/src/server/api/mastodon/MastodonConverters.ts
@@ -5,7 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { Entity, MastodonEntity, MisskeyEntity } from 'megalodon';
-import mfm from '@transfem-org/sfm-js';
+import mfm from 'mfm-js';
 import { MastodonNotificationType } from 'megalodon/lib/src/mastodon/notification.js';
 import { NotificationType } from 'megalodon/lib/src/notification.js';
 import { DI } from '@/di-symbols.js';
diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts
index dcd4d80303..a622ae7e34 100644
--- a/packages/backend/src/server/web/FeedService.ts
+++ b/packages/backend/src/server/web/FeedService.ts
@@ -15,7 +15,7 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
 import { MfmService } from "@/core/MfmService.js";
-import { parse as mfmParse } from '@transfem-org/sfm-js';
+import { parse as mfmParse } from 'mfm-js';
 
 @Injectable()
 export class FeedService {
diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts
index f96f3977d0..af1fc4e132 100644
--- a/packages/backend/test/unit/MfmService.ts
+++ b/packages/backend/test/unit/MfmService.ts
@@ -4,7 +4,7 @@
  */
 
 import * as assert from 'assert';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { Test } from '@nestjs/testing';
 
 import { CoreModule } from '@/core/CoreModule.js';
diff --git a/packages/backend/test/unit/extract-mentions.ts b/packages/backend/test/unit/extract-mentions.ts
index 2aad89d65b..3403387e30 100644
--- a/packages/backend/test/unit/extract-mentions.ts
+++ b/packages/backend/test/unit/extract-mentions.ts
@@ -5,7 +5,7 @@
 
 import * as assert from 'assert';
 
-import { parse } from '@transfem-org/sfm-js';
+import { parse } from 'mfm-js';
 import { extractMentions } from '@/misc/extract-mentions.js';
 
 describe('Extract mentions', () => {
diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json
index 5191fe6852..5b4774c7af 100644
--- a/packages/frontend-embed/package.json
+++ b/packages/frontend-embed/package.json
@@ -11,8 +11,8 @@
 	},
 	"dependencies": {
 		"@discordapp/twemoji": "15.1.0",
-		"@phosphor-icons/web": "^2.0.3",
-		"@transfem-org/sfm-js": "0.24.5",
+		"@phosphor-icons/web": "2.1.2",
+		"mfm-js": "npm:@transfem-org/sfm-js@0.24.6",
 		"buraha": "0.0.1",
 		"frontend-shared": "workspace:*",
 		"json5": "2.2.3",
diff --git a/packages/frontend-embed/src/components/EmMfm.ts b/packages/frontend-embed/src/components/EmMfm.ts
index d377d492e0..74ae3373ef 100644
--- a/packages/frontend-embed/src/components/EmMfm.ts
+++ b/packages/frontend-embed/src/components/EmMfm.ts
@@ -5,7 +5,7 @@
 
 import { h, provide } from 'vue';
 import type { VNode, SetupContext } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { host } from '@@/js/config.js';
 import EmUrl from '@/components/EmUrl.vue';
diff --git a/packages/frontend-embed/src/components/EmNote.vue b/packages/frontend-embed/src/components/EmNote.vue
index 666cbde72d..0dc77d09a7 100644
--- a/packages/frontend-embed/src/components/EmNote.vue
+++ b/packages/frontend-embed/src/components/EmNote.vue
@@ -105,7 +105,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, ref, shallowRef } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { shouldCollapsed } from '@@/js/collapsed.js';
 import { url } from '@@/js/config.js';
diff --git a/packages/frontend-embed/src/components/EmNoteDetailed.vue b/packages/frontend-embed/src/components/EmNoteDetailed.vue
index 9f4be8c666..716a5fbf81 100644
--- a/packages/frontend-embed/src/components/EmNoteDetailed.vue
+++ b/packages/frontend-embed/src/components/EmNoteDetailed.vue
@@ -128,7 +128,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, ref } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { computeMergedCw } from '@@/js/compute-merged-cw.js';
 import I18n from '@/components/I18n.vue';
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index d04a04c78c..fd99ed2194 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -112,7 +112,7 @@
 		"@vue/compiler-core": "3.5.14",
 		"@vue/compiler-sfc": "3.5.14",
 		"@vue/runtime-core": "3.5.14",
-		"@transfem-org/sfm-js": "0.24.6",
+		"mfm-js": "npm:@transfem-org/sfm-js@0.24.6",
 		"acorn": "8.14.1",
 		"astring": "1.9.0",
 		"cross-env": "7.0.3",
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 37882ef5b9..ef9d4c68fa 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -180,7 +180,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, onMounted, ref, useTemplateRef, watch, provide } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { isLink } from '@@/js/is-link.js';
 import { shouldCollapsed } from '@@/js/collapsed.js';
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 3f692d4964..b941d776de 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -233,7 +233,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, onMounted, provide, ref, useTemplateRef, watch } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { isLink } from '@@/js/is-link.js';
 import * as config from '@@/js/config.js';
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 13ba5ff0ba..508ee2d655 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -107,7 +107,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, useTemplateRef, toRaw } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import insertTextAtCursor from 'insert-text-at-cursor';
 import { toASCII } from 'punycode.js';
diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue
index 0780f6c910..b7eddb9536 100644
--- a/packages/frontend/src/components/MkSubNoteContent.vue
+++ b/packages/frontend/src/components/MkSubNoteContent.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref, computed, watch } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import { shouldCollapsed } from '@@/js/collapsed.js';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index 24293343ac..c554319c30 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -181,7 +181,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, onMounted, ref, useTemplateRef, watch, provide } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { isLink } from '@@/js/is-link.js';
 import { shouldCollapsed } from '@@/js/collapsed.js';
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index 3c867a9559..f678c85431 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -238,7 +238,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, onMounted, onUnmounted, onUpdated, provide, ref, useTemplateRef, watch } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { isLink } from '@@/js/is-link.js';
 import * as config from '@@/js/config.js';
diff --git a/packages/frontend/src/components/SkOldNoteWindow.vue b/packages/frontend/src/components/SkOldNoteWindow.vue
index 39ad2bd7c2..aa1da2d6e3 100644
--- a/packages/frontend/src/components/SkOldNoteWindow.vue
+++ b/packages/frontend/src/components/SkOldNoteWindow.vue
@@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { inject, onMounted, ref, shallowRef, computed } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import MkNoteSimple from '@/components/MkNoteSimple.vue';
 import MkMediaList from '@/components/MkMediaList.vue';
diff --git a/packages/frontend/src/components/global/MkMfm.ts b/packages/frontend/src/components/global/MkMfm.ts
index dea486e66d..9f92c43b68 100644
--- a/packages/frontend/src/components/global/MkMfm.ts
+++ b/packages/frontend/src/components/global/MkMfm.ts
@@ -4,7 +4,7 @@
  */
 
 import { h, defineAsyncComponent } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { host } from '@@/js/config.js';
 import CkFollowMouse from '../CkFollowMouse.vue';
diff --git a/packages/frontend/src/pages/chat/XMessage.vue b/packages/frontend/src/pages/chat/XMessage.vue
index 1536a91196..b72583214b 100644
--- a/packages/frontend/src/pages/chat/XMessage.vue
+++ b/packages/frontend/src/pages/chat/XMessage.vue
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, provide } from 'vue';
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { url } from '@@/js/config.js';
 import { isLink } from '@@/js/is-link.js';
diff --git a/packages/frontend/src/utility/check-animated-mfm.ts b/packages/frontend/src/utility/check-animated-mfm.ts
index 2614dfb4f1..371a631af7 100644
--- a/packages/frontend/src/utility/check-animated-mfm.ts
+++ b/packages/frontend/src/utility/check-animated-mfm.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 
 export function checkAnimationFromMfm(nodes: mfm.MfmNode[]): boolean {
 	const animatedNodes = mfm.extract(nodes, (node) => {
diff --git a/packages/frontend/src/utility/extract-mentions.ts b/packages/frontend/src/utility/extract-mentions.ts
index 89a5ce1df8..d518562053 100644
--- a/packages/frontend/src/utility/extract-mentions.ts
+++ b/packages/frontend/src/utility/extract-mentions.ts
@@ -5,7 +5,7 @@
 
 // test is located in test/extract-mentions
 
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 
 export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] {
 	// TODO: 重複を削除
diff --git a/packages/frontend/src/utility/extract-preview-urls.ts b/packages/frontend/src/utility/extract-preview-urls.ts
index e3bd62c993..264359f179 100644
--- a/packages/frontend/src/utility/extract-preview-urls.ts
+++ b/packages/frontend/src/utility/extract-preview-urls.ts
@@ -4,7 +4,7 @@
  */
 
 import type * as Misskey from 'misskey-js';
-import type * as mfm from '@transfem-org/sfm-js';
+import type * as mfm from 'mfm-js';
 import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
 import { getNoteUrls } from '@/utility/getNoteUrls';
 
diff --git a/packages/frontend/src/utility/extract-url-from-mfm.ts b/packages/frontend/src/utility/extract-url-from-mfm.ts
index 260dba030e..99d80f3624 100644
--- a/packages/frontend/src/utility/extract-url-from-mfm.ts
+++ b/packages/frontend/src/utility/extract-url-from-mfm.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as mfm from '@transfem-org/sfm-js';
+import * as mfm from 'mfm-js';
 
 // unique without hash
 // [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ]
-- 
cgit v1.2.3-freya

