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:
parent
5693c50d3a
commit
1702ccacd2
@ -8,8 +8,10 @@
|
|||||||
:class="{
|
:class="{
|
||||||
'goal-mode-banner--running': goalRunning,
|
'goal-mode-banner--running': goalRunning,
|
||||||
'goal-mode-banner--done': goalCompleted && !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"
|
@click.stop="goalRunning || goalCompleted ? $emit('open-goal-dialog') : null"
|
||||||
>
|
>
|
||||||
<span class="goal-mode-banner__dot" aria-hidden="true"></span>
|
<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');
|
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 collectComposerVisualHeight = () => {
|
||||||
const root = inputAreaRoot.value;
|
const root = inputAreaRoot.value;
|
||||||
const shell = compactInputShell.value;
|
const shell = compactInputShell.value;
|
||||||
@ -1049,7 +1063,9 @@ const collectComposerVisualHeight = () => {
|
|||||||
const shellRect = shell.getBoundingClientRect();
|
const shellRect = shell.getBoundingClientRect();
|
||||||
let top = shellRect.top;
|
let top = shellRect.top;
|
||||||
let bottom = shellRect.bottom;
|
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) => {
|
nodes.forEach((node) => {
|
||||||
if (!(node instanceof HTMLElement)) return;
|
if (!(node instanceof HTMLElement)) return;
|
||||||
const rect = node.getBoundingClientRect();
|
const rect = node.getBoundingClientRect();
|
||||||
@ -1258,6 +1274,13 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// skill 菜单开关会切换目标横幅收起/展开态,需重新上报高度,
|
||||||
|
// 否则展开回来时聊天区预留高度不会同步恢复。
|
||||||
|
watch(goalBannerCollapsed, async () => {
|
||||||
|
await nextTick();
|
||||||
|
emitComposerHeight();
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (hasSkillMarkdownLink.value) {
|
if (hasSkillMarkdownLink.value) {
|
||||||
renderRichEditor();
|
renderRichEditor();
|
||||||
@ -1321,6 +1344,30 @@ onBeforeUnmount(() => {
|
|||||||
background: color-mix(in srgb, var(--theme-surface-soft, #fffaf0) 92%, var(--claude-text-tertiary, #a59a86) 8%);
|
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));
|
border: 1px solid var(--theme-chip-border, rgba(118, 103, 84, 0.2));
|
||||||
user-select: none;
|
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 {
|
.goal-mode-banner--running {
|
||||||
color: var(--claude-accent, #da7756);
|
color: var(--claude-accent, #da7756);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user