feat: auto-scroll thinking block with per-block lock
This commit is contained in:
parent
e7a81f00e7
commit
05b404687a
@ -202,6 +202,8 @@ async function bootstrapApp() {
|
|||||||
|
|
||||||
// 设置菜单状态
|
// 设置菜单状态
|
||||||
settingsOpen: false,
|
settingsOpen: false,
|
||||||
|
// 思考块滚动锁
|
||||||
|
thinkingScrollLocks: new Map(),
|
||||||
|
|
||||||
// 工具控制菜单
|
// 工具控制菜单
|
||||||
toolMenuOpen: false,
|
toolMenuOpen: false,
|
||||||
@ -732,6 +734,7 @@ async function bootstrapApp() {
|
|||||||
this.autoScrollEnabled = true;
|
this.autoScrollEnabled = true;
|
||||||
this.userScrolling = false;
|
this.userScrolling = false;
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
|
this.thinkingScrollLocks.set(blockId, true);
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -747,8 +750,12 @@ async function bootstrapApp() {
|
|||||||
lastAction.content += data.content;
|
lastAction.content += data.content;
|
||||||
}
|
}
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
|
if (lastAction && lastAction.blockId) {
|
||||||
|
this.$nextTick(() => this.scrollThinkingToBottom(lastAction.blockId));
|
||||||
|
} else {
|
||||||
this.conditionalScrollToBottom();
|
this.conditionalScrollToBottom();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 思考结束
|
// 思考结束
|
||||||
@ -765,6 +772,7 @@ async function bootstrapApp() {
|
|||||||
this.expandedBlocks.delete(lastAction.blockId);
|
this.expandedBlocks.delete(lastAction.blockId);
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
this.$nextTick(() => this.scrollThinkingToBottom(lastAction.blockId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg.streamingThinking = '';
|
msg.streamingThinking = '';
|
||||||
@ -1295,6 +1303,7 @@ async function bootstrapApp() {
|
|||||||
if (this.markdownCache) {
|
if (this.markdownCache) {
|
||||||
this.markdownCache.clear();
|
this.markdownCache.clear();
|
||||||
}
|
}
|
||||||
|
this.thinkingScrollLocks.clear();
|
||||||
|
|
||||||
// 强制更新视图
|
// 强制更新视图
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
@ -3001,6 +3010,23 @@ async function bootstrapApp() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
scrollThinkingToBottom(blockId) {
|
||||||
|
if (!this.thinkingScrollLocks.get(blockId)) return;
|
||||||
|
const refName = `thinkingContent-${blockId}`;
|
||||||
|
const elRef = this.$refs[refName];
|
||||||
|
const el = Array.isArray(elRef) ? elRef[0] : elRef;
|
||||||
|
if (el) {
|
||||||
|
el.scrollTop = el.scrollHeight;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleThinkingScroll(blockId, event) {
|
||||||
|
const el = event.target;
|
||||||
|
const threshold = 12;
|
||||||
|
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
|
||||||
|
this.thinkingScrollLocks.set(blockId, atBottom);
|
||||||
|
},
|
||||||
|
|
||||||
// 面板调整方法
|
// 面板调整方法
|
||||||
startResize(panel, event) {
|
startResize(panel, event) {
|
||||||
this.isResizing = true;
|
this.isResizing = true;
|
||||||
|
|||||||
@ -298,7 +298,10 @@
|
|||||||
<span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
|
<span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsible-content">
|
<div class="collapsible-content">
|
||||||
<div class="content-inner thinking-content">
|
<div class="content-inner thinking-content"
|
||||||
|
:ref="`thinkingContent-${index}-thinking-${actionIndex}`"
|
||||||
|
@scroll="handleThinkingScroll(`${index}-thinking-${actionIndex}`, $event)"
|
||||||
|
style="max-height: 240px; overflow-y: auto;">
|
||||||
{{ action.content }}
|
{{ action.content }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -239,6 +239,7 @@ async function bootstrapApp() {
|
|||||||
|
|
||||||
// 设置菜单状态
|
// 设置菜单状态
|
||||||
settingsOpen: false,
|
settingsOpen: false,
|
||||||
|
thinkingScrollLocks: new Map(),
|
||||||
|
|
||||||
// 工具控制菜单
|
// 工具控制菜单
|
||||||
toolMenuOpen: false,
|
toolMenuOpen: false,
|
||||||
@ -1089,6 +1090,7 @@ async function bootstrapApp() {
|
|||||||
this.autoScrollEnabled = true;
|
this.autoScrollEnabled = true;
|
||||||
this.userScrolling = false;
|
this.userScrolling = false;
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
|
this.thinkingScrollLocks.set(blockId, true);
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1104,8 +1106,12 @@ async function bootstrapApp() {
|
|||||||
lastAction.content += data.content;
|
lastAction.content += data.content;
|
||||||
}
|
}
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
|
if (lastAction && lastAction.blockId) {
|
||||||
|
this.$nextTick(() => this.scrollThinkingToBottom(lastAction.blockId));
|
||||||
|
} else {
|
||||||
this.conditionalScrollToBottom();
|
this.conditionalScrollToBottom();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 思考结束
|
// 思考结束
|
||||||
@ -1122,6 +1128,7 @@ async function bootstrapApp() {
|
|||||||
this.expandedBlocks.delete(lastAction.blockId);
|
this.expandedBlocks.delete(lastAction.blockId);
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
this.$nextTick(() => this.scrollThinkingToBottom(lastAction.blockId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg.streamingThinking = '';
|
msg.streamingThinking = '';
|
||||||
@ -1645,6 +1652,7 @@ async function bootstrapApp() {
|
|||||||
if (this.markdownCache) {
|
if (this.markdownCache) {
|
||||||
this.markdownCache.clear();
|
this.markdownCache.clear();
|
||||||
}
|
}
|
||||||
|
this.thinkingScrollLocks.clear();
|
||||||
|
|
||||||
// 强制更新视图
|
// 强制更新视图
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
@ -3204,6 +3212,23 @@ async function bootstrapApp() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
scrollThinkingToBottom(blockId) {
|
||||||
|
if (!this.thinkingScrollLocks.get(blockId)) return;
|
||||||
|
const refName = `thinkingContent-${blockId}`;
|
||||||
|
const elRef = this.$refs[refName];
|
||||||
|
const el = Array.isArray(elRef) ? elRef[0] : elRef;
|
||||||
|
if (el) {
|
||||||
|
el.scrollTop = el.scrollHeight;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleThinkingScroll(blockId, event) {
|
||||||
|
const el = event.target;
|
||||||
|
const threshold = 12;
|
||||||
|
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
|
||||||
|
this.thinkingScrollLocks.set(blockId, atBottom);
|
||||||
|
},
|
||||||
|
|
||||||
// 面板调整方法
|
// 面板调整方法
|
||||||
startResize(panel, event) {
|
startResize(panel, event) {
|
||||||
this.isResizing = true;
|
this.isResizing = true;
|
||||||
|
|||||||
@ -270,7 +270,10 @@
|
|||||||
<span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
|
<span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsible-content">
|
<div class="collapsible-content">
|
||||||
<div class="content-inner thinking-content">
|
<div class="content-inner thinking-content"
|
||||||
|
:ref="`thinkingContent-${index}-thinking-${actionIndex}`"
|
||||||
|
@scroll="handleThinkingScroll(`${index}-thinking-${actionIndex}`, $event)"
|
||||||
|
style="max-height: 240px; overflow-y: auto;">
|
||||||
{{ action.content }}
|
{{ action.content }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user