agent-Specialization/static/src/styles/components/sidebar/_conversation.scss
JOJO b4030c386e refactor(frontend): 颜色 token 系统两层重构 + 三主题填色 + 去半透明
将前端配色从散落的硬编码/双命名体系(--claude-* / --theme-*)重构为
两层 token 结构(原始层 + 中性语义层),并按"经典抄 Claude、浅色抄
ChatGPT、深色自研"的定位完成三主题填色,同时建立 stylelint 防回退栏杆。
代码大量由 AI 生成的历史遗留配色问题(同语义多值、跨主题塌缩、强调色
撞文字色、fallback 第二真相源等)在此一并收口。

== 一、Token 结构重构(_tokens.scss)==
- 重写为两层:原始层 + 语义层;组件只引用中性语义名
  (--surface-* / --text-* / --border-* / --accent* / --state-* 等)。
- --claude-* / --theme-* 降级为兼容别名(41 个转发),迁移完成后删除。
- 三主题(classic / light / dark)+ 首屏回退(:root:not([data-theme]),
  与经典同值)全部补齐,结构对齐、不再缺项。

== 二、三主题填色 ==
- 经典 = Claude 官网亮色盘:暖奶油表面阶梯
  canvas #faf9f5 < soft #f5f0e8 < card #efe9de < cream-strong #e8e0d2
  < 纯白浮起;文字 #141413;hairline #e6dfd8;暖橙 primary #cc785c。
- 浅色 = ChatGPT 亮色盘:冷白 #ffffff / 侧栏 #f9f9f9 / 嵌套 #f3f3f3,
  中性灰文字,近黑 primary #181818。
- 深色 = 自研中性灰阶,本轮完全不动(原样保留)。
- 修复塌缩 bug:强调色在 light/dark 原被误填成灰(CTA 失去品牌色),
  现统一回品牌 primary;light 表面原全塌成 #ffffff(过于平面),
  现按灰阶拉开层次。
- 强调色克制(CTA-only voltage):primary 仅用于发送按钮/logo 等极少
  数处;hover / 选中 / 运行态一律走中性灰(运行态指示器从 amber 改灰)。
- 对话区背景从纯白改为暖奶油 canvas;个人空间下拉菜单、git 状态条、
  二级菜单等收口到统一语义色。

== 三、全面去半透明 ==
- 所有实体面板/二级菜单背景改为不透明(surface token 由半透明白改实色,
  --mobile-menu 三亮色主题改实色)。
- 移除已不透明面板上失效的 backdrop-filter 磨砂(移动端菜单、右键菜单、
  模型模式下拉等)。
- 遮罩 scrim(--overlay-scrim)与刻意玻璃质感装饰保留半透明。

== 四、块展开底色统一 ==
- 思考/工具块展开区底色统一为对话背景色:.collapsible-block 及其 header
  背景由 --surface-soft 改 transparent,与堆叠块(stacked-block)一致,
  消除"单块奶油底 vs 多块透明"的不一致。

== 五、布局修复 ==
- 工作区隐藏后残留空白:.workspace-panel--collapsed 补 flex-basis:0 +
  width/min-width:0,修正 flex-basis:auto 被子元素内容宽度撑开导致内联
  width:0 失效、collapsed 后仍占 ~300px 的问题。

== 六、防回退栏杆(新增)==
- 新增 .stylelintrc.cjs:三条规则拦截裸 hex / rgb()|hsl() 字面色 /
  var(--x, fallback) 兜底 / prefers-color-scheme;存量未清理文件用
  BASELINE_EXEMPT 临时豁免(现仅剩 5 个 admin 页面,本轮按用户要求不处理),
  清理一个移除一个、不再回退。
- package.json:build 脚本接入 stylelint(tsc → stylelint → vite),
  新增 lint:css 脚本及 stylelint/postcss-scss/postcss-html 依赖。
- 修复 25 处 \!important 笔误(zsh 历史扩展污染导致的无效 CSS)。

== 七、辅助脚本(新增 scripts/)==
- lint_check.py:Node 环境不可用时的 stylelint 替身(复刻三规则 + 括号
  校验,支持多行声明检测)。
- color_map.py:硬编码色 → token 反查,判定可映射/野色。
- strip_fallback.py:安全剥离已定义 token 的冗余 fallback。

== 八、文档 ==
- CLAUDE.md / AGENTS.md 前端设计规范更新:颜色规则扩写为三主题定位 +
  两层 token + 表面层次 + 强调色克制 + color-mix 派生 + stylelint 栏杆;
  新增"实体面板禁止半透明"规则;"禁止边缘光晕"补充"仅在用户明确允许
  或要求时才可使用"。

注:前端构建验证(npm run build / lint:css)由用户本机完成;AI 工具
上下文 Node v24 段错误无法运行 node/npm/vite/stylelint。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 16:20:58 +08:00

749 lines
18 KiB
SCSS

/* Modern reveal conversation sidebar */
.conversation-sidebar {
--conversation-collapsed-width: 52px;
--conversation-expanded-width: 306px;
--conversation-icon-cell: 40px;
--conversation-row-height: 40px;
position: fixed;
left: 0;
top: 0;
z-index: 120;
width: var(--conversation-expanded-width);
height: var(--app-viewport) !important;
min-height: var(--app-viewport) !important;
overflow: hidden;
background: var(--claude-left-rail);
border-right: 0;
color: var(--claude-text);
transition: width 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.conversation-sidebar::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 1px;
pointer-events: none;
opacity: 1;
background: var(--claude-border);
transition: opacity 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.conversation-sidebar.collapsed {
width: var(--conversation-collapsed-width);
}
.conversation-sidebar.collapsed::after {
opacity: 0;
}
.conversation-sidebar-inner {
width: var(--conversation-expanded-width);
height: 100%;
display: flex;
flex-direction: column;
padding: 18px 8px 14px 6px;
background: inherit;
}
.conversation-primary-actions {
display: flex;
flex-direction: column;
gap: 2px;
flex: 0 0 auto;
}
.sidebar-nav-row,
.search-input-wrap,
.conversation-sidebar .conversation-item {
width: 100%;
min-height: var(--conversation-row-height);
border: 0;
border-radius: 12px;
background: transparent;
color: var(--claude-text);
display: grid;
align-items: center;
}
.sidebar-nav-row {
grid-template-columns: var(--conversation-icon-cell) minmax(0, 1fr);
padding: 0;
cursor: pointer;
text-align: left;
transition:
background 140ms ease,
color 140ms ease;
}
.sidebar-nav-icon {
width: var(--conversation-icon-cell);
height: var(--conversation-row-height);
border-radius: 12px;
display: grid;
place-items: center;
color: currentColor;
transition:
background 140ms ease,
color 140ms ease;
}
.conversation-sidebar.collapsed .sidebar-nav-row:hover,
.conversation-sidebar.collapsed .sidebar-nav-row:focus-visible {
background: transparent;
}
.conversation-sidebar.collapsed .sidebar-nav-row:hover .sidebar-nav-icon,
.conversation-sidebar.collapsed .sidebar-nav-row:focus-visible .sidebar-nav-icon {
background: var(--theme-tab-active);
}
.conversation-sidebar:not(.collapsed) .sidebar-nav-row:hover,
.conversation-sidebar:not(.collapsed) .sidebar-nav-row:focus-visible,
.conversation-sidebar:not(.collapsed) .sidebar-nav-row.active {
background: var(--theme-tab-active);
outline: none;
}
.conversation-sidebar:not(.collapsed) .workspace-control-btn.active {
background: transparent;
}
.conversation-sidebar:not(.collapsed) .workspace-control-btn.active:hover,
.conversation-sidebar:not(.collapsed) .workspace-control-btn.active:focus-visible {
background: var(--theme-tab-active);
}
.sidebar-nav-label,
.conversation-search,
.conversation-list,
.personal-label {
opacity: 1;
transition: opacity 220ms ease;
transition-delay: 80ms;
}
.conversation-sidebar.collapsed .sidebar-nav-label,
.conversation-sidebar.collapsed .conversation-search,
.conversation-sidebar.collapsed .conversation-list,
.conversation-sidebar.collapsed .personal-label {
opacity: 0;
transition-delay: 0ms;
}
.sidebar-nav-label {
min-width: 0;
padding-left: 8px;
padding-right: 12px;
font-size: 15px;
line-height: 1;
letter-spacing: -0.015em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.chat-icon svg {
width: 29px;
height: 29px;
}
.conversation-menu-btn .chat-icon svg {
transform: translateY(3px);
}
.pencil-icon svg {
width: 22px;
height: 22px;
}
.monitor-mode-btn.blocked {
color: var(--claude-text-tertiary);
opacity: 0.6;
}
.conversation-search {
flex: 0 0 auto;
margin-top: 10px;
}
.search-input-wrap {
grid-template-columns: var(--conversation-icon-cell) minmax(0, 1fr);
height: var(--conversation-row-height);
overflow: hidden;
transition: background 140ms ease;
}
.search-input-wrap:hover,
.search-input-wrap:focus-within {
background: var(--theme-tab-active);
}
.search-inline-icon {
color: var(--claude-text-secondary);
}
.search-input {
min-width: 0;
width: calc(var(--conversation-expanded-width) - var(--conversation-icon-cell) - 22px);
height: var(--conversation-row-height);
border: 0;
outline: 0;
padding: 0 12px 0 8px;
background: transparent;
color: var(--claude-text);
font-size: 14px;
letter-spacing: -0.01em;
}
.search-input::placeholder {
color: var(--claude-text-secondary);
}
.conversation-sidebar .conversation-list {
min-height: 0;
flex: 1 1 auto;
overflow-y: auto;
overflow-x: hidden;
padding-top: 12px;
scrollbar-width: none !important;
-ms-overflow-style: none;
}
.conversation-sidebar .conversation-list::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
display: none !important;
}
.conversation-sidebar .conversation-list-items {
position: relative;
}
.conversation-sidebar .running-task-section {
margin: 0 8px 12px;
padding: 6px 0 8px;
border-bottom: 1px solid color-mix(in srgb, var(--claude-border) 72%, transparent);
}
.conversation-sidebar .running-task-section-title {
margin: 0 8px 4px;
font-size: 12px;
font-weight: 700;
color: var(--claude-text-secondary);
letter-spacing: 0.01em;
}
.conversation-sidebar .running-task-item {
position: relative;
display: grid;
width: 100%;
grid-template-columns: minmax(0, 1fr);
gap: 3px;
margin: 1px 0;
padding: 8px 10px 8px 12px;
border: 0;
border-radius: 12px;
background: transparent;
color: var(--claude-text);
text-align: left;
cursor: pointer;
transition:
background 140ms ease,
color 140ms ease;
}
.conversation-sidebar .running-task-item:hover,
.conversation-sidebar .running-task-item.active {
background: var(--theme-tab-active);
}
.conversation-sidebar .running-task-workspace {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 11px;
font-weight: 600;
color: var(--claude-text-secondary);
}
.conversation-sidebar .running-task-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
line-height: 1.25;
letter-spacing: -0.01em;
color: var(--claude-text);
}
.conversation-sidebar .running-task-main {
display: grid;
grid-template-columns: minmax(0, 1fr) 18px;
align-items: center;
gap: 8px;
}
.conversation-sidebar .running-task-state {
justify-self: center;
}
[data-theme='dark'] .conversation-sidebar .running-task-item:hover,
[data-theme='dark'] .conversation-sidebar .running-task-item.active,
body[data-theme='dark'] .conversation-sidebar .running-task-item:hover,
body[data-theme='dark'] .conversation-sidebar .running-task-item.active {
background: var(--hover-bg);
}
.conversation-sidebar .conversation-item {
position: relative;
grid-template-columns: minmax(0, 1fr) 40px;
margin: 1px 0;
padding: 0;
cursor: pointer;
background: transparent !important;
border: 0 !important;
box-shadow: none !important;
transition:
background 140ms ease,
color 140ms ease;
}
.conversation-sidebar .conversation-delete-move {
transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1);
}
.conversation-sidebar .animation-mode-delete .conversation-delete-move {
transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1) 100ms;
}
.conversation-sidebar .conversation-delete-enter-active {
transition:
transform 420ms cubic-bezier(0.22, 1, 0.36, 1),
opacity 260ms ease;
}
.conversation-sidebar
.animation-mode-duplicate
.conversation-delete-enter-active.insert-from-under {
transition-delay: 300ms;
}
.conversation-sidebar .conversation-delete-enter-active.insert-from-under {
z-index: 0;
}
.conversation-sidebar .conversation-delete-enter-from.insert-from-left {
transform: translateX(-112%);
opacity: 0;
}
.conversation-sidebar .conversation-delete-enter-from.insert-from-under {
transform: translateY(-100%);
opacity: 0;
}
.conversation-sidebar .conversation-item.duplicate-source-mask {
z-index: 2;
}
.conversation-sidebar .conversation-item.duplicate-source-mask::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
border-radius: 12px;
background: var(--claude-left-rail);
pointer-events: none;
}
.conversation-sidebar .conversation-item.duplicate-source-mask:hover::after {
background: var(--theme-tab-active);
}
:root[data-theme='dark']
.conversation-sidebar
.conversation-item.duplicate-source-mask:hover::after,
body[data-theme='dark']
.conversation-sidebar
.conversation-item.duplicate-source-mask:hover::after {
background: var(--hover-bg);
}
.conversation-sidebar .conversation-item.duplicate-source-mask > * {
position: relative;
z-index: 2;
}
.conversation-sidebar .conversation-delete-leave-active {
position: absolute;
left: 0;
right: 0;
width: 100%;
z-index: 1;
pointer-events: none;
transition:
transform 420ms cubic-bezier(0.32, 0, 0.67, 0),
opacity 260ms ease;
}
.conversation-sidebar .conversation-delete-leave-to {
transform: translateX(-112%);
opacity: 0;
}
.conversation-sidebar .conversation-item.deleting {
transform: translateX(-112%);
opacity: 0;
pointer-events: none;
transition:
transform 420ms cubic-bezier(0.32, 0, 0.67, 0),
opacity 260ms ease;
}
.conversation-sidebar .conversation-item:hover,
.conversation-sidebar .conversation-item:focus-within,
.conversation-sidebar .conversation-item.active:hover,
.conversation-sidebar .conversation-item.active:focus-within {
background: var(--theme-tab-active) !important;
box-shadow: none !important;
}
.conversation-sidebar .conversation-item.active {
background: var(--theme-tab-active) !important;
box-shadow: none !important;
}
:root[data-theme='dark'] .conversation-sidebar .conversation-item.active,
body[data-theme='dark'] .conversation-sidebar .conversation-item.active {
background: var(--hover-bg) !important;
box-shadow: none !important;
}
:root[data-theme='dark'] .conversation-sidebar .conversation-item:hover,
:root[data-theme='dark'] .conversation-sidebar .conversation-item:focus-within,
:root[data-theme='dark'] .conversation-sidebar .conversation-item.active:hover,
:root[data-theme='dark'] .conversation-sidebar .conversation-item.active:focus-within,
body[data-theme='dark'] .conversation-sidebar .conversation-item:hover,
body[data-theme='dark'] .conversation-sidebar .conversation-item:focus-within,
body[data-theme='dark'] .conversation-sidebar .conversation-item.active:hover,
body[data-theme='dark'] .conversation-sidebar .conversation-item.active:focus-within {
background: var(--hover-bg) !important;
box-shadow: none !important;
}
.conversation-sidebar .conversation-title {
min-width: 0;
padding-left: 12px;
padding-right: 4px;
color: var(--claude-text);
font-size: 14px;
line-height: 1.25;
letter-spacing: -0.01em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.conversation-sidebar .conversation-action-wrap {
position: relative;
width: 40px;
height: 34px;
display: grid;
place-items: center;
justify-self: start;
}
.conversation-sidebar .conversation-running-loader {
width: 16px;
height: 16px;
border: 2px solid var(--claude-text-secondary);
border-bottom-color: transparent;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: conversation-running-rotation 1s linear infinite;
}
.conversation-sidebar .conversation-complete-check {
width: 18px;
height: 18px;
display: inline-block;
background: var(--claude-text-secondary);
mask: url('/static/icons/check.svg') center / contain no-repeat;
-webkit-mask: url('/static/icons/check.svg') center / contain no-repeat;
}
@keyframes conversation-running-rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.conversation-sidebar .conversation-more-btn {
width: 30px;
height: 30px;
border: 0;
border-radius: 9px;
background: transparent;
color: var(--claude-text-secondary);
display: grid;
place-items: center;
cursor: pointer;
opacity: 0;
transition:
opacity 160ms ease,
color 140ms ease;
}
.conversation-sidebar .conversation-item:hover .conversation-more-btn,
.conversation-sidebar .conversation-action-wrap.menu-open .conversation-more-btn {
opacity: 1;
}
.conversation-sidebar .conversation-more-btn:hover,
.conversation-sidebar .conversation-action-wrap.menu-open .conversation-more-btn {
color: var(--claude-text);
}
.conversation-sidebar .conversation-more-btn span {
position: relative;
display: inline-block;
width: 22px;
height: 22px;
}
.conversation-sidebar .conversation-more-btn span::before {
content: '';
position: absolute;
left: 4px;
top: 10px;
width: 3.5px;
height: 3.5px;
border-radius: 999px;
background: currentColor;
box-shadow:
5.5px 0 0 currentColor,
11px 0 0 currentColor;
}
.conversation-sidebar .conversation-actions-menu {
position: absolute;
right: 0;
top: calc(100% + 6px);
z-index: 140;
width: max-content;
min-width: 92px;
padding: 6px;
border: 1px solid var(--claude-border);
border-radius: 14px;
background: var(--theme-surface-strong);
box-shadow: none;
opacity: 0;
transform: translateY(-4px) scale(0.98);
pointer-events: none;
transition:
opacity 120ms ease,
transform 150ms cubic-bezier(0.22, 1, 0.36, 1);
}
.conversation-sidebar .conversation-actions-menu.open {
opacity: 1;
transform: translateY(0) scale(1);
pointer-events: auto;
}
.conversation-sidebar .conversation-actions-menu button {
width: 100%;
min-width: 80px;
height: 36px;
border: 0;
border-radius: 10px;
background: transparent;
color: var(--claude-text);
display: grid;
grid-template-columns: 24px auto;
align-items: center;
gap: 4px;
padding: 0 10px 0 8px;
text-align: left;
cursor: pointer;
font-size: 14px;
transition: background 140ms ease;
}
.conversation-sidebar .conversation-actions-menu button:hover {
background: var(--theme-tab-active);
}
.conversation-sidebar .conversation-actions-menu button.danger {
color: var(--state-danger);
}
.conversation-personal-entry {
margin-top: 8px;
flex: 0 0 auto;
}
.personal-page-btn svg {
width: 22px;
height: 22px;
}
.loading-conversations,
.no-conversations,
.search-done,
.search-progress {
color: var(--claude-text-secondary);
font-size: 13px;
padding: 10px 12px;
white-space: nowrap;
}
.load-more {
padding: 10px 0;
}
.load-more-btn {
width: 100%;
height: 34px;
border: 0;
border-radius: 10px;
padding: 0 12px;
background: transparent;
color: var(--claude-text-secondary);
cursor: pointer;
text-align: left;
transition:
background 140ms ease,
color 140ms ease;
}
.load-more-btn:hover:not(:disabled) {
background: var(--theme-tab-active);
color: var(--claude-text);
}
.load-more-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.search-spinner {
width: 16px;
height: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
}
.search-spinner-orbit {
position: absolute;
inset: 0;
}
.search-spinner-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation: search-wobble 0.9s ease-in-out infinite;
}
@keyframes search-wobble {
from {
transform: translate(-50%, -50%) translateX(-1px);
}
50% {
transform: translate(-50%, -50%) translateX(1px);
}
to {
transform: translate(-50%, -50%) translateX(-1px);
}
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
/* Final sidebar-specific guards against broad legacy rules. */
.conversation-sidebar .search-input {
background: transparent !important;
box-shadow: none !important;
border: 0 !important;
}
.conversation-sidebar .search-input:focus {
background: transparent !important;
box-shadow: none !important;
}
.conversation-sidebar .conversation-list {
scrollbar-width: none !important;
-ms-overflow-style: none;
}
.conversation-sidebar .conversation-list::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
display: none !important;
}
.conversation-sidebar .personal-page-btn,
.conversation-sidebar .conversation-personal-entry:not(.collapsed) .personal-page-btn {
background: transparent !important;
box-shadow: none !important;
}
.conversation-sidebar.collapsed .personal-page-btn:hover,
.conversation-sidebar.collapsed .personal-page-btn:focus-visible {
background: transparent !important;
}
.conversation-sidebar.collapsed .personal-page-btn:hover .sidebar-nav-icon,
.conversation-sidebar.collapsed .personal-page-btn:focus-visible .sidebar-nav-icon {
background: var(--theme-tab-active) !important;
}
.conversation-sidebar:not(.collapsed) .personal-page-btn:hover,
.conversation-sidebar:not(.collapsed) .personal-page-btn:focus-visible {
background: var(--theme-tab-active) !important;
}
:root[data-theme='dark'] .conversation-sidebar:not(.collapsed) .personal-page-btn:hover,
:root[data-theme='dark'] .conversation-sidebar:not(.collapsed) .personal-page-btn:focus-visible,
body[data-theme='dark'] .conversation-sidebar:not(.collapsed) .personal-page-btn:hover,
body[data-theme='dark'] .conversation-sidebar:not(.collapsed) .personal-page-btn:focus-visible {
background: var(--hover-bg) !important;
}
.conversation-sidebar .load-more-btn {
background: transparent !important;
border: 0 !important;
box-shadow: none !important;
text-align: left !important;
}
.conversation-sidebar .load-more-btn:hover:not(:disabled) {
background: var(--theme-tab-active) !important;
}
:root[data-theme='dark'] .conversation-sidebar .load-more-btn:hover:not(:disabled),
body[data-theme='dark'] .conversation-sidebar .load-more-btn:hover:not(:disabled) {
background: var(--hover-bg) !important;
}