fix(ui): 目标横幅在队列/skill菜单展开时收起为圆点且不顶高布局

目标模式就绪横幅原为流内元素,撑高 stadium-input-wrapper,使锚定其顶部的
消息队列与 skill 菜单被顶起,且其高度被计入聊天区预留高度,进而抬高可滚动范围。

- 收起态切为 absolute 脱离文档流,锚定输入框上方,圆点位置不变、文字收拢
- collectComposerVisualHeight 排除收起态横幅,不再计入预留高度
- 新增 watch(goalBannerCollapsed) 在 skill 菜单开关时重新上报高度

队列为空且无 skill 选择时自动展开恢复原样。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
JOJO 2026-05-30 21:51:32 +08:00
parent 5693c50d3a
commit 1702ccacd2

View File

@ -8,8 +8,10 @@
:class="{
'goal-mode-banner--running': goalRunning,
'goal-mode-banner--done': goalCompleted && !goalRunning,
'goal-mode-banner--clickable': goalRunning || goalCompleted
'goal-mode-banner--clickable': goalRunning || goalCompleted,
'goal-mode-banner--collapsed': goalBannerCollapsed
}"
:title="goalBannerCollapsed ? goalBannerTitle : undefined"
@click.stop="goalRunning || goalCompleted ? $emit('open-goal-dialog') : null"
>
<span class="goal-mode-banner__dot" aria-hidden="true"></span>
@ -1040,6 +1042,18 @@ const hasRuntimeLayoutExpansion = computed(() => {
const goalCompleted = computed(() => String(props.goalProgress?.status || '').toLowerCase() === 'done');
// skill
// / skill
const goalBannerCollapsed = computed(
() => runtimeQueuedMessagesForRender.value.length > 0 || skillSlashMenuOpen.value
);
const goalBannerTitle = computed(() => {
if (props.goalRunning) return '目标模式运行中';
if (goalCompleted.value) return '目标模式完成';
return '目标模式已就绪';
});
const collectComposerVisualHeight = () => {
const root = inputAreaRoot.value;
const shell = compactInputShell.value;
@ -1049,7 +1063,9 @@ const collectComposerVisualHeight = () => {
const shellRect = shell.getBoundingClientRect();
let top = shellRect.top;
let bottom = shellRect.bottom;
const nodes = root.querySelectorAll('.runtime-queue-list:not(.runtime-queue-list--empty), .skill-slash-menu, .goal-mode-banner');
//
// .goal-mode-banner--collapsed
const nodes = root.querySelectorAll('.runtime-queue-list:not(.runtime-queue-list--empty), .skill-slash-menu, .goal-mode-banner:not(.goal-mode-banner--collapsed)');
nodes.forEach((node) => {
if (!(node instanceof HTMLElement)) return;
const rect = node.getBoundingClientRect();
@ -1258,6 +1274,13 @@ watch(
}
);
// skill /
//
watch(goalBannerCollapsed, async () => {
await nextTick();
emitComposerHeight();
});
onMounted(() => {
if (hasSkillMarkdownLink.value) {
renderRichEditor();
@ -1321,6 +1344,30 @@ onBeforeUnmount(() => {
background: color-mix(in srgb, var(--theme-surface-soft, #fffaf0) 92%, var(--claude-text-tertiary, #a59a86) 8%);
border: 1px solid var(--theme-chip-border, rgba(118, 103, 84, 0.2));
user-select: none;
transition: gap 0.2s ease, padding 0.2s ease;
}
/* padding
关键切到 absolute 脱离文档流避免横幅占用一行流高度把队列/skill 菜单顶起来
锚定在输入框上方 6px 4px与展开时的视觉位置一致圆点位置不变 */
.goal-mode-banner--collapsed {
position: absolute;
left: 4px;
bottom: calc(100% + 6px);
z-index: 2;
margin: 0;
gap: 0;
padding-right: 10px;
}
.goal-mode-banner__text {
overflow: hidden;
white-space: nowrap;
max-width: 200px;
opacity: 1;
transition: max-width 0.2s ease, opacity 0.18s ease;
}
.goal-mode-banner--collapsed .goal-mode-banner__text {
max-width: 0;
opacity: 0;
}
.goal-mode-banner--running {
color: var(--claude-accent, #da7756);