69 lines
2.4 KiB
Vue
69 lines
2.4 KiB
Vue
<script setup lang="ts">
|
|
import type { MessagePart } from '~/composables/useChat';
|
|
import ReasoningBody from './ReasoningBody.vue';
|
|
|
|
const props = defineProps<{
|
|
part: Readonly<MessagePart>;
|
|
}>();
|
|
|
|
const reasoningOpen = ref(!props.part.finished);
|
|
const bodyRef = ref<InstanceType<typeof ReasoningBody> | null>(null);
|
|
|
|
watch(
|
|
() => props.part.finished,
|
|
() => {
|
|
reasoningOpen.value = !props.part.finished;
|
|
},
|
|
);
|
|
|
|
const toggleReasoning = async () => {
|
|
if (!props.part.finished) return;
|
|
|
|
reasoningOpen.value = !reasoningOpen.value;
|
|
if (!reasoningOpen.value) return;
|
|
await nextTick();
|
|
bodyRef.value?.scrollToBottom('instant');
|
|
bodyRef.value?.handleScroll();
|
|
};
|
|
|
|
const formatDuration = (ms: number) => {
|
|
const seconds = ms / 1000;
|
|
const minutes = Math.floor(seconds / 60);
|
|
const hours = Math.floor(minutes / 60);
|
|
|
|
if (hours > 0) {
|
|
return `${hours}h ${minutes % 60}m ${(seconds % 60).toFixed(1)}s`;
|
|
} else if (minutes > 0) {
|
|
return `${minutes}m ${(seconds % 60).toFixed(1)}s`;
|
|
} else {
|
|
return `${seconds.toFixed(1)}s`;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="text-[--text-tertiary]">
|
|
<button @click="toggleReasoning" :class="[
|
|
'w-full @hover:bg-[var(--color-hover)] rounded-lg p-1 flex justify-between items-center transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]',
|
|
part.finished ? '' : 'cursor-default'
|
|
]">
|
|
<span class="flex items-center gap-1">
|
|
<span
|
|
class="w-6 h-6 flex-shrink-0 rounded-lg overflow-hidden bg-[var(--bg-surface)] flex items-center justify-center">
|
|
<span class="i-mynaui-atom text-3 text-[var(--reasoning-accent)]"></span>
|
|
</span>
|
|
<span v-if="!part.finished" class="animate-pulse">
|
|
Deep Thinking
|
|
</span>
|
|
<span v-else>
|
|
Deeply Thought (in {{ formatDuration(new Date(part.lastUpdatedAt).getTime() - new
|
|
Date(part.createdAt).getTime()) }})
|
|
</span>
|
|
</span>
|
|
<span class="i-mynaui-chevron-down w-4 h-4 text-[var(--text-secondary)]"
|
|
:class="reasoningOpen ? '' : '-rotate-90'"></span>
|
|
</button>
|
|
<ReasoningBody v-if="reasoningOpen" ref="bodyRef" :part="part" />
|
|
</div>
|
|
</template>
|