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 @@