From e0f2c0445cbbab6ccc675c1aa6ccfbae4b6863cd Mon Sep 17 00:00:00 2001
From: yas-ako <105139975+yas-ako@users.noreply.github.com>
Date: Wed, 31 Dec 2025 22:00:09 +0900
Subject: [PATCH 01/19] =?UTF-8?q?refactor:=20useMessageScroller=20?=
=?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90=E3=81=97=E3=80=81=E3=83=A1=E3=83=83?=
=?UTF-8?q?=E3=82=BB=E3=83=BC=E3=82=B8=E3=81=AE=E3=82=B9=E3=82=AF=E3=83=AD?=
=?UTF-8?q?=E3=83=BC=E3=83=AB=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8B=E5=87=A6?=
=?UTF-8?q?=E7=90=86=E3=82=92=E7=B5=B1=E5=90=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../MainView/MessageElement/ClipElement.vue | 5 +-
.../MessageElement/MessageElement.vue | 5 +-
.../MessagesScroller/MessagesScroller.vue | 80 ++--------
.../composables/useMessageScroller.ts | 140 ++++++++++++++++++
...useMessageScrollerElementResizeObserver.ts | 68 ---------
5 files changed, 157 insertions(+), 141 deletions(-)
create mode 100644 src/components/Main/MainView/MessagesScroller/composables/useMessageScroller.ts
delete mode 100644 src/components/Main/MainView/MessagesScroller/composables/useMessageScrollerElementResizeObserver.ts
diff --git a/src/components/Main/MainView/MessageElement/ClipElement.vue b/src/components/Main/MainView/MessageElement/ClipElement.vue
index 328825d9e9..6c3df45523 100644
--- a/src/components/Main/MainView/MessageElement/ClipElement.vue
+++ b/src/components/Main/MainView/MessageElement/ClipElement.vue
@@ -40,8 +40,9 @@ import type { MessageId } from '/@/types/entity-ids'
import MessageQuoteListItemFooter from './Embeddings/MessageQuoteListItemFooter.vue'
import MessageContents from './MessageContents.vue'
import MessageTools, { useMessageToolsHover } from './MessageTools.vue'
-import type { ChangeHeightData } from './composables/useElementRenderObserver'
-import useElementRenderObserver from './composables/useElementRenderObserver'
+import useElementRenderObserver, {
+ type ChangeHeightData
+} from './composables/useElementRenderObserver'
const props = defineProps<{
messageId: MessageId
diff --git a/src/components/Main/MainView/MessageElement/MessageElement.vue b/src/components/Main/MainView/MessageElement/MessageElement.vue
index 266e052c85..f3b0fa9cb0 100644
--- a/src/components/Main/MainView/MessageElement/MessageElement.vue
+++ b/src/components/Main/MainView/MessageElement/MessageElement.vue
@@ -55,8 +55,9 @@ import type { MessageId, UserId } from '/@/types/entity-ids'
import MessageContents from './MessageContents.vue'
import MessagePinned from './MessagePinned.vue'
import MessageStampList from './MessageStampList.vue'
-import type { ChangeHeightData } from './composables/useElementRenderObserver'
-import useElementRenderObserver from './composables/useElementRenderObserver'
+import useElementRenderObserver, {
+ type ChangeHeightData
+} from './composables/useElementRenderObserver'
const props = withDefaults(
defineProps<{
diff --git a/src/components/Main/MainView/MessagesScroller/MessagesScroller.vue b/src/components/Main/MainView/MessagesScroller/MessagesScroller.vue
index 00e21cb7e8..bb58394326 100644
--- a/src/components/Main/MainView/MessagesScroller/MessagesScroller.vue
+++ b/src/components/Main/MainView/MessagesScroller/MessagesScroller.vue
@@ -34,7 +34,7 @@
diff --git a/src/lib/basic/timer.ts b/src/lib/basic/timer.ts
index 212bc7a926..de8c832a5a 100644
--- a/src/lib/basic/timer.ts
+++ b/src/lib/basic/timer.ts
@@ -1,3 +1,5 @@
+import type { Invocable } from '/@/types/utility'
+
export const wait = (ms: number) =>
new Promise(resolve => {
setTimeout(() => {
@@ -5,7 +7,18 @@ export const wait = (ms: number) =>
}, ms)
})
-export const rAF = () =>
+export const nextFrame = () =>
new Promise(resolve => {
requestAnimationFrame(resolve)
})
+
+export async function defer(): Promise
+export async function defer(
+ callback: Fn
+): Promise>>
+
+export async function defer(callback?: Invocable) {
+ await nextFrame()
+ await nextFrame()
+ return callback?.()
+}
diff --git a/tests/unit/lib/basic/timer.spec.ts b/tests/unit/lib/basic/timer.spec.ts
index 92ac018ee9..c0fe831bc0 100644
--- a/tests/unit/lib/basic/timer.spec.ts
+++ b/tests/unit/lib/basic/timer.spec.ts
@@ -1,4 +1,4 @@
-import { rAF, wait } from '/@/lib/basic/timer'
+import { nextFrame, wait } from '/@/lib/basic/timer'
describe('wait', () => {
it('can wait', async () => {
@@ -11,14 +11,14 @@ describe('wait', () => {
})
})
-describe('rAF', () => {
+describe('nextFrame', () => {
beforeEach(() => {
const spy = vi.spyOn(global, 'requestAnimationFrame')
spy.mockClear()
})
it('can wait', async () => {
- const p = rAF()
+ const p = nextFrame()
expect(requestAnimationFrame).toHaveBeenCalledTimes(1)
expect(requestAnimationFrame).toHaveBeenLastCalledWith(expect.any(Function))
From 406ee008b6ae9e875fab53db4c7d0b5512a37110 Mon Sep 17 00:00:00 2001
From: uni-kakurenbo
Date: Fri, 2 Jan 2026 16:03:35 +0900
Subject: [PATCH 06/19] =?UTF-8?q?refactor:=20=E3=82=B3=E3=83=BC=E3=83=89?=
=?UTF-8?q?=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=81=8C=E5=AD=98=E5=9C=A8?=
=?UTF-8?q?=E3=81=99=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=AB=E3=82=82=E9=AB=98?=
=?UTF-8?q?=E3=81=95=E3=82=92=E7=9B=A3=E8=A6=96=E3=81=99=E3=82=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../composables/useElementRenderObserver.ts | 6 ++++--
src/composables/message/useEmbeddings.ts | 8 ++++++--
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/components/Main/MainView/MessageElement/composables/useElementRenderObserver.ts b/src/components/Main/MainView/MessageElement/composables/useElementRenderObserver.ts
index 7f3ae20394..98eae97cb3 100644
--- a/src/components/Main/MainView/MessageElement/composables/useElementRenderObserver.ts
+++ b/src/components/Main/MainView/MessageElement/composables/useElementRenderObserver.ts
@@ -21,6 +21,7 @@ const useElementRenderObserver = (
quoteMessageIds: readonly MessageId[]
fileIds: readonly FileId[]
externalUrls: readonly ExternalUrl[]
+ hasCodeBlock: boolean
}>,
emit: ((name: 'entryMessageLoaded', relativePos: number) => void) &
((name: 'changeHeight', data: ChangeHeightData) => void)
@@ -72,11 +73,12 @@ const useElementRenderObserver = (
(unref(isEntryMessage) ||
embeddingsState.quoteMessageIds.length > 0 ||
embeddingsState.fileIds.length > 0 ||
- embeddingsState.externalUrls.length > 0) &&
+ embeddingsState.externalUrls.length > 0 ||
+ embeddingsState.hasCodeBlock) &&
bodyRef.value
) {
/*
- 引用 / 添付ファイル / 外部URL がある場合か
+ 引用 / 添付ファイル / 外部URL / コードブロック がある場合か
エントリーメッセージは
高さ監視をする
*/
diff --git a/src/composables/message/useEmbeddings.ts b/src/composables/message/useEmbeddings.ts
index fe822a0256..7f4c5cbf19 100644
--- a/src/composables/message/useEmbeddings.ts
+++ b/src/composables/message/useEmbeddings.ts
@@ -5,9 +5,12 @@ import { useMessagesView } from '/@/store/domain/messagesView'
import type { MessageId } from '/@/types/entity-ids'
const useEmbeddings = (props: { messageId: MessageId }) => {
- const { embeddingsMap } = useMessagesView()
+ const { embeddingsMap, renderedContentMap } = useMessagesView()
const embeddings = computed(() => embeddingsMap.value.get(props.messageId))
+ const renderedContent = computed(
+ () => renderedContentMap.value.get(props.messageId) ?? ''
+ )
const state = reactive({
fileIds: computed(
() => embeddings.value?.filter(isFile).map(e => e.id) ?? []
@@ -21,7 +24,8 @@ const useEmbeddings = (props: { messageId: MessageId }) => {
embeddings.value?.filter(isExternalUrl).map(e => e.url) ?? []
)
].slice(0, 2)
- )
+ ),
+ hasCodeBlock: computed(() => renderedContent.value.includes(''))
})
return { embeddingsState: state }
}
From 7efb494cc5063363311fba2505b2c99d4f33f010 Mon Sep 17 00:00:00 2001
From: uni-kakurenbo
Date: Fri, 2 Jan 2026 15:44:12 +0900
Subject: [PATCH 07/19] =?UTF-8?q?fix:=20`ResizeObserver`=20=E3=81=AE?=
=?UTF-8?q?=E7=99=BB=E9=8C=B2=E3=82=92=E5=BE=85=E3=81=A3=E3=81=A6=E3=81=8B?=
=?UTF-8?q?=E3=82=89=E6=8F=8F=E7=94=BB=E3=81=99=E3=82=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../MessageElement/MessageContents.vue | 35 ++++++++++---------
src/components/UI/DeferredRender.vue | 11 ++++++
src/components/UI/MarkdownContent.vue | 16 +++------
3 files changed, 34 insertions(+), 28 deletions(-)
create mode 100644 src/components/UI/DeferredRender.vue
diff --git a/src/components/Main/MainView/MessageElement/MessageContents.vue b/src/components/Main/MainView/MessageElement/MessageContents.vue
index 1f3bf65b3b..14ce045c3e 100644
--- a/src/components/Main/MainView/MessageElement/MessageContents.vue
+++ b/src/components/Main/MainView/MessageElement/MessageContents.vue
@@ -16,22 +16,24 @@
:channel-id="message.channelId"
@finish-editing="finishEditing"
/>
-
-
-
+
+
+
+
+
@@ -39,6 +41,7 @@
diff --git a/src/components/UI/MarkdownContent.vue b/src/components/UI/MarkdownContent.vue
index 5e0d23391f..4002a18643 100644
--- a/src/components/UI/MarkdownContent.vue
+++ b/src/components/UI/MarkdownContent.vue
@@ -10,6 +10,8 @@
diff --git a/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue b/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue
new file mode 100644
index 0000000000..c555ece077
--- /dev/null
+++ b/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
diff --git a/src/components/Main/MainView/MessagesScroller/composables/useMessageScroller.ts b/src/components/Main/MainView/MessagesScroller/composables/useMessageScroller.ts
index 9cc0fb7a29..00f0bc7110 100644
--- a/src/components/Main/MainView/MessagesScroller/composables/useMessageScroller.ts
+++ b/src/components/Main/MainView/MessagesScroller/composables/useMessageScroller.ts
@@ -1,9 +1,10 @@
import type { Reactive, Ref } from 'vue'
import { reactive, watch } from 'vue'
+import { ref } from 'vue'
import type { ChangeHeightData } from '/@/components/Main/MainView/MessageElement/composables/useElementRenderObserver'
import type { LoadingDirection } from '/@/components/Main/MainView/MessagesScroller/composables/useMessagesFetcher'
-import { nextFrame } from '/@/lib/basic/timer'
+import { defer, wait } from '/@/lib/basic/timer'
import type { MessageId } from '/@/types/entity-ids'
const useMessageScroller = (
@@ -14,6 +15,8 @@ const useMessageScroller = (
entryMessageId?: MessageId
}>
) => {
+ const ready = ref(false)
+
const state = reactive({
height: 0,
scrollTop: 0
@@ -118,12 +121,21 @@ const useMessageScroller = (
}
if (scrollerProps.lastLoadingDirection === 'latest') {
+ ready.value = false
+
// チャンネルを移動したとき、
rootRef.value.scrollTo({
top: newHeight
})
state.height = newHeight
+
+ defer(async () => {
+ await defer()
+ await wait(400)
+
+ ready.value = true
+ })
}
} else state.height = newHeight
},
@@ -133,6 +145,7 @@ const useMessageScroller = (
return {
onChangeHeight,
onEntryMessageLoaded,
+ ready,
state
}
}
From d0d38145becaae36a8f69771d81600699031520c Mon Sep 17 00:00:00 2001
From: uni-kakurenbo
Date: Sat, 3 Jan 2026 12:59:14 +0900
Subject: [PATCH 11/19] =?UTF-8?q?feat:=20=E3=82=B9=E3=82=B1=E3=83=AB?=
=?UTF-8?q?=E3=83=88=E3=83=B3=E3=82=92=E3=83=AA=E3=83=83=E3=83=81=E3=81=AB?=
=?UTF-8?q?=E3=81=99=E3=82=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../MessagesScroller/MessagesSkeleton.vue | 122 +++++++++++++++---
1 file changed, 104 insertions(+), 18 deletions(-)
diff --git a/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue b/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue
index c555ece077..c7c66c8b6a 100644
--- a/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue
+++ b/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue
@@ -1,25 +1,66 @@
-
+
diff --git a/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue b/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue
index c7c66c8b6a..695ccebfdf 100644
--- a/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue
+++ b/src/components/Main/MainView/MessagesScroller/MessagesSkeleton.vue
@@ -1,10 +1,10 @@
-