- 设置与内容状态改为 localStorage 同步缓存(主题缓存同模式): 自动展开设置、全局内容状态、按对话 ID 内容状态(LRU 上限 50) - 三态乐观掩码:assumedActive 生效时 hasContent 完全由缓存假定值 决定(含假定无内容),dock 与对话区同刻切换收起/展开 - 无过渡窗口重写为可重入机制:初始加载与切换对话(flush:sync) 两个时机开启,按 filesSyncSeq/todoSyncSeq 序号推进精确关窗 - 跨工作区点选对话时保持当前对话视图冻结,不再经过空对话 中间态,消除输入栏先上后下
1005 lines
20 KiB
CSS
1005 lines
20 KiB
CSS
/* ============================================================
|
||
快捷窗口(Quick Dock)共享样式
|
||
配色全部走三主题语义 token;动画参数迁移自 demo/todo_float_window
|
||
============================================================ */
|
||
|
||
.quick-dock,
|
||
.qd-detail,
|
||
.qd-preview,
|
||
.qd-menu {
|
||
--qd-row-h: 36px;
|
||
--qd-width: 270px;
|
||
--qd-ease-out: cubic-bezier(0.22, 0.9, 0.28, 1);
|
||
--qd-ease-strike: cubic-bezier(0.55, 0.06, 0.35, 1);
|
||
}
|
||
|
||
/* ---------------- 占位列容器 ---------------- */
|
||
|
||
.quick-dock {
|
||
flex: none;
|
||
width: 294px; /* 270 + 左右 12 padding */
|
||
/* top 44px:避开右上角展开/收起悬浮按钮(top 8 + 高 28 + 8 间距) */
|
||
padding: 44px 12px 20px 0;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
transition:
|
||
width 0.3s ease,
|
||
padding 0.3s ease,
|
||
opacity 0.25s ease,
|
||
transform 0.3s var(--qd-ease-out);
|
||
}
|
||
|
||
/* 四个窗口都无内容时收起到 0 宽,不挤压对话区。
|
||
上下 padding 保持与展开态一致(44/20):若收起态 padding 归 0,
|
||
展开过渡中 padding-top 0→44px 会让内容垂直下移 44px,叠加 translateX
|
||
形成「从右上角斜向出现」的观感;保留后动画只剩纯水平分量(从右到左) */
|
||
.quick-dock--empty {
|
||
width: 0;
|
||
padding: 44px 0 20px;
|
||
opacity: 0;
|
||
transform: translateX(24px);
|
||
}
|
||
|
||
/* 初始加载窗口内禁用容器过渡:进入页面时设置与首批内容异步到达,
|
||
首次状态校正必须瞬间完成,否则会看到「先空再展开」/「先展开再收起」的闪烁;
|
||
窗口关闭(类移除)后恢复正常交互动画。规则位于 .quick-dock 之后,同优先级覆盖 */
|
||
.quick-dock--no-anim {
|
||
transition: none;
|
||
}
|
||
|
||
.quick-dock__scroll {
|
||
flex: 1;
|
||
min-height: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
gap: 12px;
|
||
overflow-y: auto;
|
||
scrollbar-width: none;
|
||
-ms-overflow-style: none;
|
||
/* padding + 负 margin:扩大滚动盒以容纳窗口阴影,避免被 overflow 裁剪 */
|
||
padding: 24px;
|
||
margin: -24px;
|
||
}
|
||
|
||
.quick-dock__scroll::-webkit-scrollbar {
|
||
display: none;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
/* ---------------- 窗口通用 ---------------- */
|
||
|
||
.qd-window {
|
||
flex: none; /* 禁止被列 flex 压缩变矮 */
|
||
width: var(--qd-width);
|
||
background: var(--surface-raised);
|
||
border: 1px solid var(--border-default);
|
||
border-radius: 12px;
|
||
box-shadow: var(--shadow-card);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.qd-window.qd-window-enter {
|
||
animation: qd-window-in 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes qd-window-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-6px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.qd-window__header {
|
||
height: 38px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
padding: 0 12px;
|
||
border-bottom: 1px solid var(--border-default);
|
||
}
|
||
|
||
.qd-window__icon {
|
||
width: 15px;
|
||
height: 15px;
|
||
color: var(--accent);
|
||
flex: none;
|
||
}
|
||
|
||
.qd-window__title {
|
||
font-size: 12.5px;
|
||
font-weight: 600;
|
||
color: var(--text-secondary);
|
||
letter-spacing: 0.02em;
|
||
}
|
||
|
||
.qd-window__counter {
|
||
margin-left: auto;
|
||
font-size: 11.5px;
|
||
color: var(--text-tertiary);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
/* 列表:固定 5 行,无上下 padding(首行紧贴标题分隔线),滚动条隐藏 */
|
||
.qd-list {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
height: calc(var(--qd-row-h) * 5);
|
||
overflow-y: auto;
|
||
scrollbar-width: none;
|
||
-ms-overflow-style: none;
|
||
}
|
||
|
||
.qd-list::-webkit-scrollbar {
|
||
display: none;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
/* 交互区域禁止文字选择:点击条目展开详情/预览时,防止 mousedown 起点落在文字上,
|
||
DOM 动画期间选择范围异常扩展,把条目与详情文字整片选中。
|
||
详情输出与预览代码内容保持可选,不影响复制。 */
|
||
.qd-window__header,
|
||
.qd-run-item,
|
||
.qd-todo-item,
|
||
.qd-file-item,
|
||
.qd-detail__header,
|
||
.qd-preview__header,
|
||
.qd-menu {
|
||
user-select: none;
|
||
}
|
||
|
||
/* ---------------- 行进入 / 移出动画(待办 / 子智能体 / 后台指令 / 文件共用) ---------------- */
|
||
|
||
.qd-row-enter {
|
||
opacity: 0;
|
||
animation: qd-row-in 0.34s var(--qd-ease-out) forwards;
|
||
}
|
||
|
||
@keyframes qd-row-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateX(-14px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
|
||
.qd-row-leave {
|
||
animation: qd-row-out 0.26s ease-in forwards;
|
||
}
|
||
|
||
@keyframes qd-row-out {
|
||
0% {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
height: var(--qd-row-h);
|
||
}
|
||
55% {
|
||
opacity: 0;
|
||
transform: translateX(-14px);
|
||
height: var(--qd-row-h);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translateX(-14px);
|
||
height: 0;
|
||
}
|
||
}
|
||
|
||
/* ---------------- 待办行 ---------------- */
|
||
|
||
.qd-todo-item {
|
||
height: var(--qd-row-h);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 9px;
|
||
padding: 0 12px;
|
||
overflow: hidden; /* 配合移出动画的高度收拢 */
|
||
}
|
||
|
||
.qd-todo-dot {
|
||
flex: none;
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
border: 1.5px solid var(--text-tertiary);
|
||
background: transparent;
|
||
transition:
|
||
background 0.2s ease,
|
||
border-color 0.2s ease;
|
||
}
|
||
|
||
.qd-todo-item.is-done .qd-todo-dot {
|
||
background: var(--accent);
|
||
border-color: var(--accent);
|
||
}
|
||
|
||
.qd-todo-text {
|
||
position: relative;
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-size: 13px;
|
||
line-height: 1;
|
||
color: var(--text-primary);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
transition: color 0.22s ease 0.12s; /* 变灰略滞后于划线 */
|
||
}
|
||
|
||
.qd-todo-text::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: -1px;
|
||
right: -1px;
|
||
top: 50%;
|
||
height: 1.5px;
|
||
margin-top: -0.75px;
|
||
background: var(--text-tertiary);
|
||
border-radius: 1px;
|
||
transform: scaleX(0);
|
||
transform-origin: left center;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* 完成静态态(整表渲染时已完成行直接呈现,不播划线) */
|
||
.qd-todo-item.is-done .qd-todo-text {
|
||
color: var(--text-tertiary);
|
||
}
|
||
|
||
.qd-todo-item.is-done .qd-todo-text::after {
|
||
transform: scaleX(1);
|
||
}
|
||
|
||
/* 完成动作瞬间:横线从左往右划过 + 状态点弹跳 */
|
||
.qd-todo-item.just-done .qd-todo-text::after {
|
||
animation: qd-strike-in 0.28s var(--qd-ease-strike) forwards;
|
||
}
|
||
|
||
@keyframes qd-strike-in {
|
||
from {
|
||
transform: scaleX(0);
|
||
}
|
||
to {
|
||
transform: scaleX(1);
|
||
}
|
||
}
|
||
|
||
.qd-todo-item.just-done .qd-todo-dot {
|
||
animation: qd-dot-pop 0.32s var(--qd-ease-out);
|
||
}
|
||
|
||
@keyframes qd-dot-pop {
|
||
0% {
|
||
transform: scale(1);
|
||
}
|
||
45% {
|
||
transform: scale(1.6);
|
||
}
|
||
100% {
|
||
transform: scale(1);
|
||
}
|
||
}
|
||
|
||
/* ---------------- 子智能体 / 后台指令行 ---------------- */
|
||
|
||
.qd-run-item {
|
||
height: var(--qd-row-h);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
padding: 0 8px 0 12px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: background 0.12s ease;
|
||
}
|
||
|
||
.qd-run-item:hover {
|
||
background: var(--hover-bg);
|
||
}
|
||
|
||
.qd-run-item.is-active {
|
||
background: var(--surface-soft);
|
||
}
|
||
|
||
.qd-run-status {
|
||
flex: none;
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: transparent;
|
||
}
|
||
|
||
/* 状态配色对齐旧工作区面板 .sub-agent-status:
|
||
运行中=info 蓝 / 空闲=次要文字白 / 完成=success 绿 / 失败·超时·终止=danger 红 */
|
||
.qd-run-item.is-running .qd-run-status {
|
||
background: var(--state-info);
|
||
animation: qd-status-pulse 1.6s ease-in-out infinite;
|
||
}
|
||
|
||
.qd-run-item.is-idle .qd-run-status {
|
||
background: var(--text-secondary);
|
||
}
|
||
|
||
.qd-run-item.is-done .qd-run-status {
|
||
background: var(--state-success);
|
||
}
|
||
|
||
.qd-run-item.is-ended .qd-run-status {
|
||
background: var(--state-danger);
|
||
}
|
||
|
||
@keyframes qd-status-pulse {
|
||
0%,
|
||
100% {
|
||
opacity: 1;
|
||
}
|
||
50% {
|
||
opacity: 0.3;
|
||
}
|
||
}
|
||
|
||
.qd-run-name {
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-size: 13px;
|
||
color: var(--text-primary);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.qd-run-item[data-kind='cmd'] .qd-run-name {
|
||
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.qd-run-item.is-done .qd-run-name,
|
||
.qd-run-item.is-ended .qd-run-name {
|
||
color: var(--text-tertiary);
|
||
}
|
||
|
||
/* ⋯ 按钮(行内) */
|
||
.qd-row-menu-btn {
|
||
flex: none;
|
||
width: 22px;
|
||
height: 22px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
background: none;
|
||
border: none;
|
||
border-radius: 5px;
|
||
color: var(--text-tertiary);
|
||
cursor: pointer;
|
||
transition:
|
||
background 0.12s ease,
|
||
color 0.12s ease;
|
||
}
|
||
|
||
.qd-row-menu-btn svg {
|
||
width: 14px;
|
||
height: 14px;
|
||
}
|
||
|
||
.qd-row-menu-btn:hover {
|
||
background: var(--tab-active);
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
/* ---------------- 文件行 ---------------- */
|
||
|
||
.qd-file-item {
|
||
height: var(--qd-row-h);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
padding: 0 10px 0 6px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: background 0.12s ease;
|
||
}
|
||
|
||
.qd-file-item:hover {
|
||
background: var(--hover-bg);
|
||
}
|
||
|
||
.qd-file-item.is-active {
|
||
background: var(--surface-soft);
|
||
}
|
||
|
||
.qd-file-name {
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-size: 13px;
|
||
color: var(--text-primary);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
/* ---------------- 详情面板(fixed 浮在列左侧) ---------------- */
|
||
|
||
.qd-detail {
|
||
position: fixed;
|
||
top: 20px;
|
||
right: calc(294px + 12px); /* 快捷窗口列宽 + gap */
|
||
width: 480px;
|
||
max-width: calc(100vw - 360px);
|
||
height: 460px;
|
||
max-height: calc(100vh - 40px);
|
||
background: var(--surface-raised);
|
||
border: 1px solid var(--border-default);
|
||
border-radius: 12px;
|
||
box-shadow: var(--shadow-card);
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
z-index: 90;
|
||
}
|
||
|
||
.qd-detail.panel-enter {
|
||
animation: qd-panel-in 0.24s var(--qd-ease-out);
|
||
}
|
||
|
||
@keyframes qd-panel-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateX(12px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
|
||
.qd-detail.panel-leave {
|
||
animation: qd-panel-out 0.18s ease-in forwards;
|
||
}
|
||
|
||
@keyframes qd-panel-out {
|
||
to {
|
||
opacity: 0;
|
||
transform: translateX(8px);
|
||
}
|
||
}
|
||
|
||
.qd-detail__header {
|
||
flex: none;
|
||
height: 42px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 0 12px;
|
||
border-bottom: 1px solid var(--border-default);
|
||
}
|
||
|
||
.qd-detail__dot {
|
||
flex: none;
|
||
width: 7px;
|
||
height: 7px;
|
||
border-radius: 50%;
|
||
background: var(--text-tertiary);
|
||
}
|
||
|
||
.qd-detail__dot.is-running {
|
||
background: var(--state-info);
|
||
animation: qd-status-pulse 1.6s ease-in-out infinite;
|
||
}
|
||
|
||
.qd-detail__dot.is-idle {
|
||
background: var(--text-secondary);
|
||
}
|
||
|
||
.qd-detail__dot.is-done {
|
||
background: var(--state-success);
|
||
}
|
||
|
||
.qd-detail__dot.is-ended {
|
||
background: var(--state-danger);
|
||
}
|
||
|
||
.qd-detail__title {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
min-width: 0;
|
||
}
|
||
|
||
.qd-detail__badge {
|
||
flex: none;
|
||
font-size: 11px;
|
||
line-height: 1;
|
||
padding: 4px 8px;
|
||
border-radius: 99px;
|
||
background: var(--surface-soft);
|
||
color: var(--text-tertiary);
|
||
}
|
||
|
||
.qd-detail__badge.is-running {
|
||
background: color-mix(in srgb, var(--state-info) 14%, transparent);
|
||
color: var(--state-info);
|
||
}
|
||
|
||
.qd-detail__badge.is-idle {
|
||
background: var(--surface-soft);
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.qd-detail__badge.is-done {
|
||
background: color-mix(in srgb, var(--state-success) 14%, transparent);
|
||
color: var(--state-success);
|
||
}
|
||
|
||
.qd-detail__badge.is-ended {
|
||
background: color-mix(in srgb, var(--state-danger) 14%, transparent);
|
||
color: var(--state-danger);
|
||
}
|
||
|
||
.qd-detail__close {
|
||
flex: none;
|
||
margin-left: auto;
|
||
width: 26px;
|
||
height: 26px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
background: none;
|
||
border: none;
|
||
border-radius: 6px;
|
||
color: var(--text-tertiary);
|
||
cursor: pointer;
|
||
transition:
|
||
background 0.12s ease,
|
||
color 0.12s ease;
|
||
}
|
||
|
||
.qd-detail__close svg {
|
||
width: 13px;
|
||
height: 13px;
|
||
}
|
||
|
||
.qd-detail__close:hover {
|
||
background: var(--tab-active);
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.qd-detail__body {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 6px 0 10px;
|
||
scrollbar-width: none;
|
||
-ms-overflow-style: none;
|
||
}
|
||
|
||
.qd-detail__body::-webkit-scrollbar {
|
||
display: none;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
.qd-detail__body.body-fade {
|
||
animation: qd-body-fade-in 0.18s ease-out;
|
||
}
|
||
|
||
@keyframes qd-body-fade-in {
|
||
from {
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.qd-detail__empty {
|
||
padding: 18px 14px;
|
||
font-size: 12.5px;
|
||
color: var(--text-tertiary);
|
||
}
|
||
|
||
/* ---------------- feed 行(扁平行式,无嵌套卡片) ---------------- */
|
||
|
||
.qd-feed-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 0 14px;
|
||
min-height: 28px;
|
||
font-size: 12.5px;
|
||
}
|
||
|
||
/* 仅「面板打开期间新出现」的行播进入动画;打开时已有的行由组件静态渲染,
|
||
避免每次打开详情全部行重播一遍出现动画 */
|
||
.qd-feed-row.is-new {
|
||
animation: qd-feed-in 0.25s var(--qd-ease-out);
|
||
}
|
||
|
||
@keyframes qd-feed-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(5px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
/* 子智能体文本输出:浅底色圆角块,与工具行/终端行的扁平行式区分 */
|
||
.qd-feed-row.feed-text {
|
||
align-items: flex-start;
|
||
margin: 3px 10px;
|
||
padding: 6px 8px;
|
||
background: var(--surface-soft);
|
||
border-radius: 8px;
|
||
color: var(--text-secondary);
|
||
line-height: 1.55;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.qd-feed-row.feed-tool .tool-name {
|
||
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
||
font-size: 12px;
|
||
color: var(--text-primary);
|
||
flex: none;
|
||
}
|
||
|
||
.qd-feed-row.feed-tool .tool-param {
|
||
color: var(--text-tertiary);
|
||
font-size: 12px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
min-width: 0;
|
||
}
|
||
|
||
.qd-feed-row.feed-tool .tool-result {
|
||
flex: none;
|
||
margin-left: auto;
|
||
font-size: 11.5px;
|
||
color: var(--text-tertiary);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.qd-feed-row.feed-tool .tool-result.is-error {
|
||
color: var(--state-danger);
|
||
}
|
||
|
||
.qd-tool-spinner {
|
||
flex: none;
|
||
width: 11px;
|
||
height: 11px;
|
||
border-radius: 50%;
|
||
border: 1.5px solid var(--border-strong);
|
||
border-top-color: var(--accent);
|
||
animation: qd-tool-spin 0.7s linear infinite;
|
||
}
|
||
|
||
@keyframes qd-tool-spin {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
.qd-tool-done {
|
||
flex: none;
|
||
width: 11px;
|
||
text-align: center;
|
||
font-size: 11px;
|
||
color: var(--accent);
|
||
}
|
||
|
||
.qd-feed-row.feed-term {
|
||
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
||
font-size: 12px;
|
||
color: var(--text-secondary);
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
min-height: 24px;
|
||
}
|
||
|
||
.qd-feed-row.feed-killed {
|
||
color: var(--state-danger);
|
||
}
|
||
|
||
/* ---------------- 文件预览侧边栏(占位列) ---------------- */
|
||
|
||
.qd-preview {
|
||
flex: none;
|
||
width: 452px; /* 440 + 右 12 padding */
|
||
max-width: calc(100vw - 200px);
|
||
padding: 20px 12px 20px 0;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
}
|
||
|
||
.qd-preview__panel {
|
||
flex: 1;
|
||
min-height: 0;
|
||
background: var(--surface-raised);
|
||
border: 1px solid var(--border-default);
|
||
border-radius: 12px;
|
||
box-shadow: var(--shadow-card);
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 预览侧边栏滑入滑出(对齐 git/终端面板 right-panels-slide 的参数) */
|
||
.qd-preview-slide-enter-active,
|
||
.qd-preview-slide-leave-active {
|
||
transition:
|
||
width 0.24s ease,
|
||
padding 0.24s ease,
|
||
opacity 0.2s ease,
|
||
transform 0.24s var(--qd-ease-out);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.qd-preview-slide-enter-from,
|
||
.qd-preview-slide-leave-to {
|
||
width: 0 !important;
|
||
padding-right: 0 !important;
|
||
opacity: 0;
|
||
transform: translateX(24px);
|
||
}
|
||
|
||
.qd-preview__header {
|
||
flex: none;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 0 14px;
|
||
border-bottom: 1px solid var(--border-default);
|
||
}
|
||
|
||
.qd-preview__header > svg {
|
||
flex: none;
|
||
width: 15px;
|
||
height: 15px;
|
||
color: var(--accent);
|
||
}
|
||
|
||
.qd-preview__name {
|
||
flex: none;
|
||
max-width: 45%;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
||
color: var(--text-primary);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.qd-preview__path {
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-size: 11.5px;
|
||
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
||
color: var(--text-tertiary);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.qd-preview__close {
|
||
flex: none;
|
||
width: 26px;
|
||
height: 26px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
background: none;
|
||
border: none;
|
||
border-radius: 6px;
|
||
color: var(--text-tertiary);
|
||
cursor: pointer;
|
||
transition:
|
||
background 0.12s ease,
|
||
color 0.12s ease;
|
||
}
|
||
|
||
.qd-preview__close svg {
|
||
width: 13px;
|
||
height: 13px;
|
||
}
|
||
|
||
.qd-preview__close:hover {
|
||
background: var(--tab-active);
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.qd-preview__body {
|
||
flex: 1;
|
||
overflow: auto;
|
||
padding: 8px 0 12px;
|
||
scrollbar-width: none;
|
||
-ms-overflow-style: none;
|
||
}
|
||
|
||
.qd-preview__body::-webkit-scrollbar {
|
||
display: none;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
.qd-preview__loading,
|
||
.qd-preview__error {
|
||
padding: 18px 14px;
|
||
font-size: 12.5px;
|
||
color: var(--text-tertiary);
|
||
}
|
||
|
||
.qd-preview__error {
|
||
color: var(--state-danger);
|
||
}
|
||
|
||
/* 行高 = 12px × 1.75 = 21px,与 FilePreviewPanel.vue 中 LINE_HEIGHT 保持同步 */
|
||
.qd-code {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
||
font-size: 12px;
|
||
line-height: 1.75;
|
||
}
|
||
|
||
.qd-code-hoverline {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
height: 21px;
|
||
background: var(--hover-bg);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.qd-code-gutter {
|
||
position: relative;
|
||
z-index: 1;
|
||
flex: none;
|
||
width: 44px;
|
||
padding-right: 12px;
|
||
box-sizing: border-box;
|
||
text-align: right;
|
||
color: var(--text-tertiary);
|
||
user-select: none;
|
||
}
|
||
|
||
.qd-code-gutter span {
|
||
display: block;
|
||
}
|
||
|
||
.qd-code-pre {
|
||
position: relative;
|
||
z-index: 1;
|
||
flex: 1;
|
||
min-width: 0;
|
||
margin: 0;
|
||
padding-right: 14px;
|
||
font: inherit;
|
||
white-space: pre;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
/* ---------------- 全局 ⋯ 菜单(fixed 单例) ---------------- */
|
||
|
||
.qd-menu {
|
||
position: fixed;
|
||
z-index: 300;
|
||
min-width: 116px;
|
||
padding: 4px;
|
||
background: var(--surface-raised);
|
||
border: 1px solid var(--border-default);
|
||
border-radius: 8px;
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.qd-menu.menu-enter {
|
||
animation: qd-menu-in 0.16s ease-out;
|
||
}
|
||
|
||
@keyframes qd-menu-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-3px) scale(0.97);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1);
|
||
}
|
||
}
|
||
|
||
.qd-menu__item {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 100%;
|
||
height: 30px;
|
||
padding: 0 10px;
|
||
background: none;
|
||
border: none;
|
||
border-radius: 5px;
|
||
font-size: 12.5px;
|
||
text-align: left;
|
||
color: var(--text-primary);
|
||
cursor: pointer;
|
||
transition: background 0.12s ease;
|
||
}
|
||
|
||
.qd-menu__item:hover:not(:disabled) {
|
||
background: var(--surface-soft);
|
||
}
|
||
|
||
.qd-menu__item--danger {
|
||
color: var(--state-danger);
|
||
}
|
||
|
||
.qd-menu__item--danger:hover:not(:disabled) {
|
||
background: color-mix(in srgb, var(--state-danger) 8%, transparent);
|
||
}
|
||
|
||
.qd-menu__item:disabled {
|
||
color: var(--text-tertiary);
|
||
cursor: default;
|
||
}
|
||
|
||
/* ---------------- 展开/收起悬浮按钮(App.vue 零宽锚点) ---------------- */
|
||
|
||
/* 零宽锚点:flex 兄弟节点,始终贴在 QuickDock 右缘;
|
||
dock 收起时 = main 右缘(落在对话标题栏右端),展开时 = dock 右缘(悬浮面板上方) */
|
||
.qd-toggle-anchor {
|
||
flex: none;
|
||
width: 0;
|
||
position: relative;
|
||
overflow: visible;
|
||
z-index: 70;
|
||
}
|
||
|
||
.qd-toggle {
|
||
position: absolute;
|
||
top: 8px;
|
||
right: 16px;
|
||
width: 28px;
|
||
height: 28px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
/* 线条色与左侧对话记录侧边栏导航图标一致(--text-primary),恒定不变:
|
||
无 hover 变色,展开(is-active)状态也不变色 */
|
||
color: var(--text-primary);
|
||
/* 底色与背后面板一致(收起=对话标题栏 / 展开=应用背景,两者同为 --surface-panel) */
|
||
background: var(--surface-panel);
|
||
border: none;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.qd-toggle svg line {
|
||
stroke: currentColor;
|
||
stroke-width: 1.8;
|
||
stroke-linecap: round;
|
||
}
|