- 四窗口固定顺序占位布局,空时收起到 0 宽;动画区分「运行中新出现」与「切换/加载」 - 文件记录:edit/write 埋点写入对话 metadata.edited_files,预览走既有 /api/file/content - 详情面板:lastDetail/lastStatus 快照、首次填充静态+瞬间到底、终态四色分类 - 数据通道全部走 REST 轮询(带 conversation_id 对话级隔离),含 demo 与设计文档
277 lines
7.5 KiB
CSS
277 lines
7.5 KiB
CSS
/* ============================================================
|
||
待办事项悬浮窗 Demo 样式
|
||
配色暂用经典主题暖色系,正式接入时替换为语义 token
|
||
============================================================ */
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
:root {
|
||
--surface-base: #faf9f5;
|
||
--surface-float: #ffffff;
|
||
--surface-wash: #f5f0e8;
|
||
--border-subtle: #ece5d8;
|
||
--border-strong: #e2d9c8;
|
||
--text-primary: #26241f;
|
||
--text-secondary: #6f6a5e;
|
||
--text-tertiary: #a9a294;
|
||
--accent: #cc785c;
|
||
--accent-hover: #c06a4e;
|
||
--hover-wash: rgba(38, 36, 31, 0.04);
|
||
--shadow-float: 0 1px 2px rgba(38, 36, 31, 0.05), 0 8px 24px rgba(38, 36, 31, 0.10);
|
||
--row-h: 36px;
|
||
--window-max-w: 300px;
|
||
--ease-out: cubic-bezier(0.22, 0.9, 0.28, 1);
|
||
--ease-strike: cubic-bezier(0.55, 0.06, 0.35, 1);
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", sans-serif;
|
||
background: var(--surface-base);
|
||
color: var(--text-primary);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ---------------- 模拟对话区 ---------------- */
|
||
|
||
.mock-chat {
|
||
max-width: 640px;
|
||
margin: 0 auto;
|
||
padding: 64px 24px 220px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 14px;
|
||
}
|
||
|
||
.mock-bubble {
|
||
max-width: 78%;
|
||
padding: 10px 14px;
|
||
border-radius: 14px;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.mock-bubble--user { align-self: flex-end; background: #efe9de; }
|
||
.mock-bubble--ai { align-self: flex-start; background: #ffffff; border: 1px solid var(--border-subtle); }
|
||
|
||
/* ---------------- 悬浮窗口栈 ---------------- */
|
||
|
||
.float-stack {
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
z-index: 100;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end; /* 多个窗口右对齐,上下排布 */
|
||
gap: 12px;
|
||
/* 整体超出屏幕高度时可滚动,滚动条隐藏 */
|
||
max-height: calc(100vh - 40px);
|
||
overflow-y: auto;
|
||
scrollbar-width: none;
|
||
-ms-overflow-style: none;
|
||
/* padding + 负 margin:扩大滚动盒以容纳窗口阴影,避免被 overflow 裁剪 */
|
||
padding: 24px;
|
||
margin: -24px;
|
||
}
|
||
|
||
.float-stack::-webkit-scrollbar { display: none; width: 0; height: 0; }
|
||
|
||
/* 窗口:圆角矩形 + 固定宽度(最大宽度的 90%) + 不透明背景 + 克制阴影 */
|
||
.float-window {
|
||
flex: none; /* 禁止被栈 flex 压缩,保持固定高度 */
|
||
width: calc(var(--window-max-w) * 0.9); /* 300 * 0.9 = 270px */
|
||
background: var(--surface-float);
|
||
border: 1px solid var(--border-subtle);
|
||
border-radius: 12px;
|
||
box-shadow: var(--shadow-float);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 窗口自身出现 / 消失(高度变化由行展开动画带动,这里只做淡入) */
|
||
.float-window.window-enter { animation: window-in 0.3s ease-out; }
|
||
@keyframes window-in {
|
||
from { opacity: 0; transform: translateY(-6px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.float-window.window-leave { animation: window-out 0.22s ease-in forwards; }
|
||
@keyframes window-out {
|
||
to { opacity: 0; transform: translateY(-4px) scale(0.98); }
|
||
}
|
||
|
||
/* 窗口标题栏 */
|
||
.float-window__header {
|
||
height: 38px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
padding: 0 12px;
|
||
border-bottom: 1px solid var(--border-subtle);
|
||
}
|
||
|
||
.float-window__icon { width: 15px; height: 15px; color: var(--accent); flex: none; }
|
||
|
||
.float-window__title {
|
||
font-size: 12.5px;
|
||
font-weight: 600;
|
||
color: var(--text-secondary);
|
||
letter-spacing: 0.02em;
|
||
}
|
||
|
||
.todo-counter {
|
||
margin-left: auto;
|
||
font-size: 11.5px;
|
||
color: var(--text-tertiary);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
/* ---------------- 待办列表 ---------------- */
|
||
|
||
/* 窗口高度固定为 5 行,不足留空、超出内部滚动;滚动条完全隐藏。
|
||
无上下 padding:首行 hover 紧贴标题栏分隔线,末行紧贴窗口底边 */
|
||
.todo-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
height: calc(var(--row-h) * 5);
|
||
overflow-y: auto;
|
||
scrollbar-width: none; /* Firefox */
|
||
-ms-overflow-style: none; /* 旧 Edge */
|
||
}
|
||
|
||
.todo-list::-webkit-scrollbar { display: none; width: 0; height: 0; }
|
||
|
||
/* 每一行直接铺在窗口上:无背景、无边框、无嵌套卡片 */
|
||
/* 待办行不可点击,无 hover 效果 */
|
||
.todo-item {
|
||
height: var(--row-h);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 9px;
|
||
padding: 0 12px;
|
||
overflow: hidden; /* 配合移出动画的高度收拢 */
|
||
}
|
||
|
||
/* 状态点 */
|
||
.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;
|
||
}
|
||
|
||
.todo-item.is-done .todo-dot { background: var(--accent); border-color: var(--accent); }
|
||
|
||
/* 任务文字:固定一行,超长省略 */
|
||
.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; /* 变灰略滞后于划线 */
|
||
}
|
||
|
||
/* 删除线(默认收起) */
|
||
.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;
|
||
}
|
||
|
||
/* 完成态(静态,用于整表渲染时已完成行) */
|
||
.todo-item.is-done .todo-text { color: var(--text-tertiary); }
|
||
.todo-item.is-done .todo-text::after { transform: scaleX(1); }
|
||
|
||
/* 完成动作瞬间:横线从左往右划过 + 状态点弹跳 */
|
||
.todo-item.just-done .todo-text::after {
|
||
animation: strike-in 0.28s var(--ease-strike) forwards;
|
||
}
|
||
@keyframes strike-in {
|
||
from { transform: scaleX(0); }
|
||
to { transform: scaleX(1); }
|
||
}
|
||
|
||
.todo-item.just-done .todo-dot { animation: dot-pop 0.32s var(--ease-out); }
|
||
@keyframes dot-pop {
|
||
0% { transform: scale(1); }
|
||
45% { transform: scale(1.6); }
|
||
100% { transform: scale(1); }
|
||
}
|
||
|
||
/* 进入动画:从左向右位移 + 淡入(stagger 由 JS 写 animation-delay) */
|
||
.todo-item.is-entering {
|
||
opacity: 0;
|
||
animation: todo-in 0.34s var(--ease-out) forwards;
|
||
}
|
||
@keyframes todo-in {
|
||
from { opacity: 0; transform: translateX(-14px); }
|
||
to { opacity: 1; transform: translateX(0); }
|
||
}
|
||
|
||
/* 移出动画:从右向左位移 + 淡出,随后高度收拢让下方行平滑上移 */
|
||
.todo-item.is-leaving {
|
||
animation: todo-out 0.26s ease-in forwards;
|
||
}
|
||
@keyframes todo-out {
|
||
0% { opacity: 1; transform: translateX(0); height: var(--row-h); }
|
||
55% { opacity: 0; transform: translateX(-14px); height: var(--row-h); }
|
||
100% { opacity: 0; transform: translateX(-14px); height: 0; }
|
||
}
|
||
|
||
/* ---------------- demo 控制台 ---------------- */
|
||
|
||
.demo-panel {
|
||
position: fixed;
|
||
left: 20px;
|
||
bottom: 20px;
|
||
z-index: 100;
|
||
}
|
||
|
||
.demo-panel__hint {
|
||
font-size: 12px;
|
||
color: var(--text-tertiary);
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.demo-panel__buttons {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
}
|
||
|
||
.demo-btn {
|
||
height: 34px;
|
||
padding: 0 14px;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--border-strong);
|
||
background: var(--surface-float);
|
||
color: var(--text-primary);
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
transition: background 0.15s ease, border-color 0.15s ease;
|
||
}
|
||
|
||
.demo-btn:hover { background: var(--surface-wash); }
|
||
|
||
.demo-btn--primary { background: var(--accent); border-color: var(--accent); color: #ffffff; }
|
||
.demo-btn--primary:hover { background: var(--accent-hover); }
|
||
|
||
.demo-btn--ghost { border-style: dashed; color: var(--text-tertiary); }
|