chore:优化书签样式

This commit is contained in:
ViperEkura 2026-03-26 17:43:06 +08:00
parent 9fb32f1074
commit a847d91886
4 changed files with 18 additions and 26 deletions

View File

@ -57,7 +57,7 @@
</template> </template>
<script setup> <script setup>
import { ref, shallowRef, computed, onMounted, defineAsyncComponent, triggerRef } from 'vue' import { ref, shallowRef, computed, onMounted, defineAsyncComponent } from 'vue'
import Sidebar from './components/Sidebar.vue' import Sidebar from './components/Sidebar.vue'
import ChatView from './components/ChatView.vue' import ChatView from './components/ChatView.vue'
@ -249,6 +249,7 @@ function createStreamCallbacks(convId, { updateConvList = true } = {}) {
return { return {
onThinkingStart() { onThinkingStart() {
updateStreamField(convId, 'streamThinking', streamThinking, '') updateStreamField(convId, 'streamThinking', streamThinking, '')
updateStreamField(convId, 'streamContent', streamContent, '')
}, },
onThinking(text) { onThinking(text) {
updateStreamField(convId, 'streamThinking', streamThinking, prev => (prev || '') + text) updateStreamField(convId, 'streamThinking', streamThinking, prev => (prev || '') + text)

View File

@ -174,21 +174,12 @@ function scrollToMessage(msgId) {
if (!scrollContainer.value) return if (!scrollContainer.value) return
const el = scrollContainer.value.querySelector(`[data-msg-id="${msgId}"]`) const el = scrollContainer.value.querySelector(`[data-msg-id="${msgId}"]`)
if (el) { if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'center' }) el.scrollIntoView({ behavior: 'smooth', block: 'start' })
activeMessageId.value = msgId activeMessageId.value = msgId
} }
}) })
} }
function scrollToBottom(smooth = true) {
nextTick(() => {
const el = scrollContainer.value
if (el) {
el.scrollTo({ top: el.scrollHeight, behavior: smooth ? 'smooth' : 'instant' })
}
})
}
// 使 instant smooth // 使 instant smooth
watch([() => props.messages.length, () => props.streamingContent], () => { watch([() => props.messages.length, () => props.streamingContent], () => {
nextTick(() => { nextTick(() => {

View File

@ -2,27 +2,31 @@
<Teleport to="body"> <Teleport to="body">
<div v-if="messages.length > 0" class="bookmark-rail"> <div v-if="messages.length > 0" class="bookmark-rail">
<div <div
v-for="(msg, idx) in messages" v-for="msg in userMessages"
:key="msg.id" :key="msg.id"
class="bookmark" class="bookmark"
:class="{ active: activeId === msg.id, user: msg.role === 'user' }" :class="{ active: activeId === msg.id }"
@click="$emit('scrollTo', msg.id)" @click="$emit('scrollTo', msg.id)"
> >
<div class="bookmark-dot"></div> <div class="bookmark-dot"></div>
<div class="bookmark-label">{{ msg.role === 'user' ? '用户' : 'Claw' }} · {{ preview(msg) }}</div> <div class="bookmark-label">{{ preview(msg) }}</div>
</div> </div>
</div> </div>
</Teleport> </Teleport>
</template> </template>
<script setup> <script setup>
defineProps({ import { computed } from 'vue'
const props = defineProps({
messages: { type: Array, required: true }, messages: { type: Array, required: true },
activeId: { type: String, default: null }, activeId: { type: String, default: null },
}) })
defineEmits(['scrollTo']) defineEmits(['scrollTo'])
const userMessages = computed(() => props.messages.filter(m => m.role === 'user'))
function preview(msg) { function preview(msg) {
if (!msg.text) return '...' if (!msg.text) return '...'
const clean = msg.text.replace(/[#*`~>\-\[\]()]/g, '').replace(/\s+/g, ' ').trim() const clean = msg.text.replace(/[#*`~>\-\[\]()]/g, '').replace(/\s+/g, ' ').trim()
@ -84,19 +88,15 @@ function preview(msg) {
} }
.bookmark-dot { .bookmark-dot {
width: 4px; width: 5px;
height: 4px; height: 5px;
border-radius: 50%; border-radius: 1.5px;
flex-shrink: 0; flex-shrink: 0;
background: var(--text-tertiary); background: #3b82f6;
opacity: 0.35; opacity: 0.35;
transition: all 0.2s ease; transition: all 0.2s ease;
} }
.bookmark.user .bookmark-dot {
background: #3b82f6;
}
.bookmark.active .bookmark-dot, .bookmark.active .bookmark-dot,
.bookmark-rail:hover .bookmark-dot { .bookmark-rail:hover .bookmark-dot {
opacity: 1; opacity: 1;

View File

@ -158,10 +158,10 @@ const processItems = computed(() => {
} }
} }
// text // text
if (props.streaming && props.streamingContent) { if (props.streaming && props.streamingContent) {
const lastStep = items[items.length - 1] const hasTextStep = items.some(it => it.type === 'text')
if (!lastStep || lastStep.type !== 'text') { if (!hasTextStep) {
items.push({ items.push({
type: 'text', type: 'text',
content: props.streamingContent, content: props.streamingContent,