From 1702ccacd22539bcc2c36efbd62f514cb984c823 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Sat, 30 May 2026 21:51:32 +0800 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=E7=9B=AE=E6=A0=87=E6=A8=AA=E5=B9=85?= =?UTF-8?q?=E5=9C=A8=E9=98=9F=E5=88=97/skill=E8=8F=9C=E5=8D=95=E5=B1=95?= =?UTF-8?q?=E5=BC=80=E6=97=B6=E6=94=B6=E8=B5=B7=E4=B8=BA=E5=9C=86=E7=82=B9?= =?UTF-8?q?=E4=B8=94=E4=B8=8D=E9=A1=B6=E9=AB=98=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 目标模式就绪横幅原为流内元素,撑高 stadium-input-wrapper,使锚定其顶部的 消息队列与 skill 菜单被顶起,且其高度被计入聊天区预留高度,进而抬高可滚动范围。 - 收起态切为 absolute 脱离文档流,锚定输入框上方,圆点位置不变、文字收拢 - collectComposerVisualHeight 排除收起态横幅,不再计入预留高度 - 新增 watch(goalBannerCollapsed) 在 skill 菜单开关时重新上报高度 队列为空且无 skill 选择时自动展开恢复原样。 Co-Authored-By: Claude Opus 4.8 (1M context) --- static/src/components/input/InputComposer.vue | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/static/src/components/input/InputComposer.vue b/static/src/components/input/InputComposer.vue index 58adda1..4dc5c57 100644 --- a/static/src/components/input/InputComposer.vue +++ b/static/src/components/input/InputComposer.vue @@ -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" > @@ -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);