fix(ui): adjust composer status layering
This commit is contained in:
parent
0815625cfd
commit
2f0e269788
@ -1,72 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="input-area compact-input-area" ref="inputAreaRoot">
|
<div class="input-area compact-input-area" ref="inputAreaRoot">
|
||||||
<div class="stadium-input-wrapper" ref="stadiumShellOuter">
|
<div class="stadium-input-wrapper" ref="stadiumShellOuter">
|
||||||
<transition name="floating-status-motion">
|
|
||||||
<div
|
|
||||||
v-if="floatingStatusVisible"
|
|
||||||
class="floating-project-status"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<span v-if="projectGitSummaryForRender" class="floating-project-status__left">
|
|
||||||
<span class="floating-project-status__menu-wrap">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="floating-project-status__button floating-project-status__button--project"
|
|
||||||
:class="{ 'floating-project-status__button--active': projectGitMenuOpen === 'project' }"
|
|
||||||
@click.stop="toggleProjectGitMenu('project')"
|
|
||||||
>
|
|
||||||
{{ projectGitSummaryForRender.projectName }}
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
v-if="projectGitMenuOpen === 'project'"
|
|
||||||
class="floating-project-status__submenu floating-project-status__submenu--project"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<button type="button" @click.stop="openProjectInFileManager">在文件管理器中打开</button>
|
|
||||||
<button type="button" @click.stop="copyProjectPath">复制地址</button>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
<span class="floating-project-status__menu-wrap">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="floating-project-status__button floating-project-status__button--branch"
|
|
||||||
:class="{ 'floating-project-status__button--active': projectGitMenuOpen === 'branch' }"
|
|
||||||
@click.stop="toggleProjectGitMenu('branch')"
|
|
||||||
>
|
|
||||||
{{ projectGitSummaryForRender.branch }}
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
v-if="projectGitMenuOpen === 'branch'"
|
|
||||||
class="floating-project-status__submenu floating-project-status__submenu--branch"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<button type="button" @click.stop="copyProjectBranch">复制分支名称</button>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span class="floating-project-status__right">
|
|
||||||
<span v-if="goalRunning" class="floating-project-status__notice">目标模式运行中</span>
|
|
||||||
<span v-else-if="goalModeArmed" class="floating-project-status__notice">目标模式就绪</span>
|
|
||||||
<button
|
|
||||||
v-if="userQuestionMinimized && pendingUserQuestionCount > 0"
|
|
||||||
type="button"
|
|
||||||
class="floating-project-status__notice floating-project-status__notice--button"
|
|
||||||
@click.stop="$emit('restore-user-question')"
|
|
||||||
>
|
|
||||||
等待回答问题
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="projectGitSummaryForRender"
|
|
||||||
type="button"
|
|
||||||
class="floating-project-status__stats"
|
|
||||||
@click.stop="$emit('open-git-changes-panel')"
|
|
||||||
>
|
|
||||||
<span class="floating-project-status__add">+{{ projectGitSummaryForRender.additions }}</span>
|
|
||||||
<span class="floating-project-status__del">-{{ projectGitSummaryForRender.deletions }}</span>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
<div
|
<div
|
||||||
class="runtime-queue-list"
|
class="runtime-queue-list"
|
||||||
:class="{ 'runtime-queue-list--empty': !runtimeQueuedMessagesForRender.length }"
|
:class="{ 'runtime-queue-list--empty': !runtimeQueuedMessagesForRender.length }"
|
||||||
@ -136,6 +70,72 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
<transition name="floating-status-motion">
|
||||||
|
<div
|
||||||
|
v-if="floatingStatusVisible"
|
||||||
|
class="floating-project-status"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<span v-if="projectGitSummaryForRender" class="floating-project-status__left">
|
||||||
|
<span class="floating-project-status__menu-wrap">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="floating-project-status__button floating-project-status__button--project"
|
||||||
|
:class="{ 'floating-project-status__button--active': projectGitMenuOpen === 'project' }"
|
||||||
|
@click.stop="toggleProjectGitMenu('project')"
|
||||||
|
>
|
||||||
|
{{ projectGitSummaryForRender.projectName }}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-if="projectGitMenuOpen === 'project'"
|
||||||
|
class="floating-project-status__submenu floating-project-status__submenu--project"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<button type="button" @click.stop="openProjectInFileManager">在文件管理器中打开</button>
|
||||||
|
<button type="button" @click.stop="copyProjectPath">复制地址</button>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span class="floating-project-status__menu-wrap">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="floating-project-status__button floating-project-status__button--branch"
|
||||||
|
:class="{ 'floating-project-status__button--active': projectGitMenuOpen === 'branch' }"
|
||||||
|
@click.stop="toggleProjectGitMenu('branch')"
|
||||||
|
>
|
||||||
|
{{ projectGitSummaryForRender.branch }}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-if="projectGitMenuOpen === 'branch'"
|
||||||
|
class="floating-project-status__submenu floating-project-status__submenu--branch"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<button type="button" @click.stop="copyProjectBranch">复制分支名称</button>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="floating-project-status__right">
|
||||||
|
<span v-if="goalRunning" class="floating-project-status__notice">目标模式运行中</span>
|
||||||
|
<span v-else-if="goalModeArmed" class="floating-project-status__notice">目标模式就绪</span>
|
||||||
|
<button
|
||||||
|
v-if="userQuestionMinimized && pendingUserQuestionCount > 0"
|
||||||
|
type="button"
|
||||||
|
class="floating-project-status__notice floating-project-status__notice--button"
|
||||||
|
@click.stop="$emit('restore-user-question')"
|
||||||
|
>
|
||||||
|
等待回答问题
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="projectGitSummaryForRender"
|
||||||
|
type="button"
|
||||||
|
class="floating-project-status__stats"
|
||||||
|
@click.stop="$emit('open-git-changes-panel')"
|
||||||
|
>
|
||||||
|
<span class="floating-project-status__add">+{{ projectGitSummaryForRender.additions }}</span>
|
||||||
|
<span class="floating-project-status__del">-{{ projectGitSummaryForRender.deletions }}</span>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
<div
|
<div
|
||||||
class="stadium-shell"
|
class="stadium-shell"
|
||||||
ref="compactInputShell"
|
ref="compactInputShell"
|
||||||
@ -1459,22 +1459,30 @@ const collectComposerVisualHeight = () => {
|
|||||||
if (!root || !shell) {
|
if (!root || !shell) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const shellRect = shell.getBoundingClientRect();
|
// 用 offset 布局坐标(offsetTop/offsetHeight)计算可视高度,而非 getBoundingClientRect。
|
||||||
let top = shellRect.top;
|
// 后者会把进入/离开动画的 transform 一起算进去:状态栏进场时还停在 translateY(44px) 的低位,
|
||||||
let bottom = shellRect.bottom;
|
// 测得的增长量≈0,要等 260ms 动画结束才量到真实高度、再迟一拍下降——表现为「先挡住、完全出现后才下降」。
|
||||||
|
// offset 系列只反映静止布局、不受 transform 影响,状态栏一挂载就按最终高度上报,
|
||||||
|
// 输入栏下降与状态栏出现动画得以同时发生。这些浮层与 shell 同为 .stadium-input-wrapper 的直接子节点,
|
||||||
|
// 其 offsetParent 一致,offsetTop 可直接比较。
|
||||||
|
let top = shell.offsetTop;
|
||||||
|
let bottom = shell.offsetTop + shell.offsetHeight;
|
||||||
const slashMenu = root.querySelector('.skill-slash-menu');
|
const slashMenu = root.querySelector('.skill-slash-menu');
|
||||||
if (slashMenu instanceof HTMLElement) {
|
if (slashMenu instanceof HTMLElement) {
|
||||||
const rect = slashMenu.getBoundingClientRect();
|
const sTop = slashMenu.offsetTop;
|
||||||
return Math.max(0, Math.max(shellRect.bottom, rect.bottom) - Math.min(shellRect.top, rect.top));
|
const sBottom = slashMenu.offsetTop + slashMenu.offsetHeight;
|
||||||
|
return Math.max(0, Math.max(bottom, sBottom) - Math.min(top, sTop));
|
||||||
}
|
}
|
||||||
// 收起态横幅是脱离流、浮在角上的小圆点,不应计入为聊天区预留的高度,
|
// 收起态横幅是脱离流、浮在角上的小圆点,不应计入为聊天区预留的高度,
|
||||||
// 否则会把消息区可滚动范围顶高。故排除 .goal-mode-banner--collapsed。
|
// 否则会把消息区可滚动范围顶高。故排除 .goal-mode-banner--collapsed。
|
||||||
const nodes = root.querySelectorAll('.runtime-queue-list:not(.runtime-queue-list--empty), .floating-project-status');
|
const nodes = root.querySelectorAll('.runtime-queue-list:not(.runtime-queue-list--empty), .floating-project-status');
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
if (!(node instanceof HTMLElement)) return;
|
if (!(node instanceof HTMLElement)) return;
|
||||||
const rect = node.getBoundingClientRect();
|
// 离开态:floatingStatusVisible 已翻 false 但元素仍在 DOM 中做退场动画。
|
||||||
top = Math.min(top, rect.top);
|
// 此时不再计入它的高度,让输入栏立即回升、与状态栏的退场动画同步。
|
||||||
bottom = Math.max(bottom, rect.bottom);
|
if (node.classList.contains('floating-project-status') && !floatingStatusVisible.value) return;
|
||||||
|
top = Math.min(top, node.offsetTop);
|
||||||
|
bottom = Math.max(bottom, node.offsetTop + node.offsetHeight);
|
||||||
});
|
});
|
||||||
return Math.max(0, bottom - top);
|
return Math.max(0, bottom - top);
|
||||||
};
|
};
|
||||||
@ -1807,9 +1815,9 @@ onBeforeUnmount(() => {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.floating-project-status {
|
.floating-project-status {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: calc(50% - 7px);
|
left: calc(5% - 7px);
|
||||||
bottom: calc(100% + 6px);
|
bottom: calc(100% + 6px);
|
||||||
z-index: 45;
|
z-index: auto;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
@ -1822,7 +1830,6 @@ onBeforeUnmount(() => {
|
|||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
background: var(--theme-surface-muted);
|
background: var(--theme-surface-muted);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
transform: translateX(-50%);
|
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1992,18 +1999,22 @@ onBeforeUnmount(() => {
|
|||||||
opacity 180ms ease,
|
opacity 180ms ease,
|
||||||
transform 260ms cubic-bezier(0.25, 0.8, 0.25, 1);
|
transform 260ms cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
transform-origin: bottom center;
|
transform-origin: bottom center;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
/* 状态栏本体永远在最底层:输入栏 shell(z-index:2) > / 菜单(z-index:1) > 状态栏(auto)。
|
||||||
|
不在这里设置 z-index,避免创建低层 stacking context 后把二级菜单也压在输入栏下。 */
|
||||||
|
z-index: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-status-motion-enter-from,
|
.floating-status-motion-enter-from,
|
||||||
.floating-status-motion-leave-to {
|
.floating-status-motion-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-50%) translateY(44px);
|
transform: translateY(44px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-status-motion-enter-to,
|
.floating-status-motion-enter-to,
|
||||||
.floating-status-motion-leave-from {
|
.floating-status-motion-leave-from {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(-50%) translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 目标模式横幅:与 + 按钮左对齐,置于输入框上方 */
|
/* 目标模式横幅:与 + 按钮左对齐,置于输入框上方 */
|
||||||
|
|||||||
@ -172,8 +172,21 @@
|
|||||||
will-change: transform, clip-path;
|
will-change: transform, clip-path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* / 菜单始终高于状态栏本体、低于输入栏 shell(z-index:2):
|
||||||
|
输入栏 > / 菜单 > 状态栏。本体状态栏不设置正 z-index,
|
||||||
|
只让状态栏二级菜单单独使用高 z-index。 */
|
||||||
|
.skill-slash-menu-motion-enter-active,
|
||||||
|
.skill-slash-menu-motion-leave-active {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.skill-slash-menu-motion-enter-from,
|
.skill-slash-menu-motion-enter-from,
|
||||||
.skill-slash-menu-motion-leave-to {
|
.skill-slash-menu-motion-leave-to {
|
||||||
|
/* 底边锚定在输入框上沿(bottom:100%-2px),配合 clip-path 从底部揭开:
|
||||||
|
translateY(100%) 把元素整体下移一个自身高度后,再让 clip 的底部 inset 从 100%→0,
|
||||||
|
净效果是「可见底边始终钉在输入框上沿、可见顶边向上生长」,即从输入框上沿升起。
|
||||||
|
注意:不能改成 translateY(0)——那样 clip 会从元素静止态的顶部(在输入框上方一整个菜单高度处)
|
||||||
|
向下揭开,看起来像菜单从高空往下展开,正是要避免的「在状态栏上方出现」。 */
|
||||||
transform: translateX(-50%) translateY(100%);
|
transform: translateX(-50%) translateY(100%);
|
||||||
clip-path: inset(0 0 100% 0 round 12px 12px 0 0);
|
clip-path: inset(0 0 100% 0 round 12px 12px 0 0);
|
||||||
}
|
}
|
||||||
@ -913,7 +926,10 @@ body[data-theme='dark'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.composer-container.blank-hero-mode {
|
.composer-container.blank-hero-mode {
|
||||||
transform: translateY(-38vh);
|
// 新对话居中态:当输入栏上方出现 git 状态栏或输入变多行时,整体可视高度会向上增长,
|
||||||
|
// 顶到欢迎文字。用已计算好的 --composer-growth-height 把输入栏整体下移同样的距离,
|
||||||
|
// 让栈顶回到「裸单行输入」时的位置,从而保证不遮挡欢迎文字。
|
||||||
|
transform: translateY(calc(-38vh + var(--composer-growth-height, 0px)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@ -925,7 +941,7 @@ body[data-theme='dark'] {
|
|||||||
height: 44px;
|
height: 44px;
|
||||||
}
|
}
|
||||||
.composer-container.blank-hero-mode {
|
.composer-container.blank-hero-mode {
|
||||||
transform: translateY(-32vh);
|
transform: translateY(calc(-32vh + var(--composer-growth-height, 0px)));
|
||||||
}
|
}
|
||||||
.blank-hero-overlay {
|
.blank-hero-overlay {
|
||||||
padding-bottom: 120px;
|
padding-bottom: 120px;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user