Compare commits

...

9 Commits

Author SHA1 Message Date
d095531bb8 refactor(frontend): unify chat content rail alignment
Normalize the chat content rail around the input composer as the width baseline. This introduces shared rail variables, compensates message content for the actual scrollbar gutter, and separates mobile/webview behavior so desktop, mobile web, and app shell layouts stay centered consistently.

Align assistant text, markdown blocks, tables, minimal-mode summaries, expanded minimal steps, slash/runtime/git floating UI, and brief-message separators to the same horizontal rail. Remove the collapsed right resize handle and mobile menu glow while preserving the intended percentage widths for floating composer affordances.

Validation: npm run build --silent 2>&1 | tail -n 5
2026-06-07 01:55:20 +08:00
05e8ea5e40 feat(frontend): 输入栏现代化重设计 + 配套高度/配色修复
- 输入栏由体育场形改为圆角矩形,输入区与按钮行分离
- +/发送按钮移至底部行(左下/右下),缩小尺寸、圆角 hover、同心圆角对齐
- 发送按钮去光晕:浅色黑底白三角、深色白底黑三角,仅留浅阴影
- logo 旁思考模式按钮去除扩散光晕,focus 仅留描边环
- 对话区预留高度基准 80→90 适配更高输入栏,欢迎语 padding 同步加大
- /菜单命令名配色:经典 accent 橙、浅/深色保留 state-info 蓝
- /菜单及深色 + 菜单 hover 统一为侧边栏同款 --theme-tab-active(修复深色全黑)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 18:25:20 +08:00
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
e34e2c965c fix(frontend): 多行输入时保持光标可见
adjustTextareaSize 重算高度会打乱 scrollTop,导致多行输入时光标被滚出视野。
新增 keepEditorSelectionVisible:高度重设后,聚焦态用编辑器 coordsAtPos 算出
光标位置并滚回可视区(上下留 lineHeight*0.3 padding),非聚焦态恢复原 scrollTop。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 23:33:48 +08:00
9be775d346 feat(personalization): 简略消息显示改为持久化到配置文件
原来「简略消息显示」只存浏览器 localStorage(experiments),换设备/清缓存即丢。
现迁移到后端 personalization.json,与主题/默认隐藏工作区一致即时保存:

- 后端新增 compact_message_display 字段(默认 full)及清洗(仅 full/brief,
  容错大小写空格,非法回退 full)
- store: PersonalForm 加字段;setCompactMessageDisplay 写 form + 即时 POST;
  加载时同步进旧 experiments 镜像,并一次性迁移本地遗留 brief 回写配置文件
- 抽屉下拉与 ChatArea 改读 form.compact_message_display,experiments 作兜底
- 保留 localStorage 镜像保证向后兼容,老用户首次加载自动迁移偏好

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 23:31:06 +08:00
9399d3f41e fix(models): preserve session model selection 2026-06-02 20:36:51 +08:00
779140ef59 fix(frontend): 新建对话时重置 token 统计
新对话流程(resetNewConversation)清空消息时一并调用 resetTokenStatistics,
避免上一对话的 token 统计残留到新对话。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 21:09:00 +08:00
469e804e38 fix(frontend): 工具块展开内部滚动条样式统一,滚动槽透明
- MinimalBlocks 思考内容(.thinking-content)补全自定义滚动条
- ToolAction 结构化显示三类滚动区(content/diff/code/output pre)统一样式
- 参考 git 状态侧边栏滚动条:10px 圆角胶囊滑块、半透明主题色
- 滚动槽(track)设为透明,贴合工具块灰色底色,避免明显槽体
- 颜色走 --claude-*/--theme-* 变量,三模式自适应

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 21:07:11 +08:00
548455f127 feat(frontend): 状态栏 +/- 行数变化加数字滚筒动画
新增 RollingNumber.vue:逐位纵向滚筒,值变大下面顶上来、变小上面压下来,
高位先动(38ms 错开),一次变化两格直达不路过中间数字。接入 InputComposer
状态栏的 +additions / -deletions 两处。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 19:15:30 +08:00
51 changed files with 3857 additions and 1715 deletions

84
.stylelintrc.cjs Normal file
View File

@ -0,0 +1,84 @@
/**
* Stylelint 配置 颜色 Token 栏杆规范见 doc/frontend/color_token_spec.md
*
* 作用拦住三类"绕过设计 Token"的写法让规范从文字变成机制
* 规则1 color-no-hex 禁止裸十六进制色号 #da7756
* 规则2 declaration-property-value-... 禁止 rgb()/hsl() 字面色禁止 var(--x, #hex) 兜底
* 规则3 media-feature-name-disallowed-list 禁止组件内新增 prefers-color-scheme 主题分支
*
* 基线策略baseline
* - 颜色属性必须走 var(--token)
* - 下方 BASELINE_EXEMPT 列出的是"立规矩之前就存在违规"的存量文件暂时豁免
* - 新文件不在名单里 从第一行起就受三条规则约束
* - 每当治理清理掉一个存量文件就把它从 BASELINE_EXEMPT 删除它从此受约束不再回退
* - 设计上永久豁免_tokens.scssToken 定义源_virtual-monitor.scsscanvas 动画资产
*/
// 颜色相关属性:这些属性的值必须用 token不能写字面色
const COLOR_PROPS =
'/^(color|fill|stroke|background|background-color|border|border-color|border-top-color|border-right-color|border-bottom-color|border-left-color|box-shadow|outline|outline-color|text-decoration-color|caret-color|column-rule-color|stop-color)$/';
// 立规矩之前就存在硬编码颜色的存量文件baseline 豁免,治理后逐个移除)
const BASELINE_EXEMPT = [
'static/src/admin/AdminDashboardApp.vue',
'static/src/admin/ApiAdminApp.vue',
'static/src/admin/CustomToolsApp.vue',
'static/src/admin/CustomToolsGuideApp.vue',
'static/src/admin/PolicyApp.vue',
];
// 三条颜色规则(供默认与 override 复用)
const COLOR_RULES = {
// 规则1禁止裸 hex
'color-no-hex': [true, { message: '禁止裸 hex 色号,请使用设计 Tokenvar(--accent) 等(见 color_token_spec.md' }],
// 规则2禁止 rgb()/hsl() 字面色 + 禁止 var() 兜底色
'declaration-property-value-disallowed-list': [
{
[COLOR_PROPS]: ['/rgba?\\(/', '/hsla?\\(/', '/var\\(\\s*--[^,)]+,/'],
},
{
message:
'颜色属性禁止 rgb()/hsl() 字面色或 var(--x, #hex) 兜底,请直接用 var(--token)(见 color_token_spec.md',
},
],
// 规则3禁止组件内手写 prefers-color-scheme 主题分支(主题切换只在 _tokens.scss 的 [data-theme] 完成)
'media-feature-name-disallowed-list': [
['prefers-color-scheme'],
{ message: '禁止在组件内用 prefers-color-scheme 写主题分支,颜色随主题变化应通过 Token 自动完成' },
],
};
// 关闭三条规则(给存量豁免文件用)
const COLOR_RULES_OFF = {
'color-no-hex': null,
'declaration-property-value-disallowed-list': null,
'media-feature-name-disallowed-list': null,
};
module.exports = {
// 设计上永久豁免Token 定义源 + canvas 动画资产
ignoreFiles: [
'static/dist/**',
'static/src/styles/base/_tokens.scss',
'static/src/styles/components/chat/_virtual-monitor.scss',
],
// 顶层 rules 必填stylelint 16 要求)。具体规则按文件类型在 overrides 中施加。
rules: {},
overrides: [
{
files: ['static/src/**/*.scss'],
customSyntax: 'postcss-scss',
rules: COLOR_RULES,
},
{
files: ['static/src/**/*.vue'],
customSyntax: 'postcss-html',
rules: COLOR_RULES,
},
// baseline存量文件暂时关闭三条规则治理后从此数组移除该文件即可恢复约束
{
files: BASELINE_EXEMPT,
rules: COLOR_RULES_OFF,
},
],
};

View File

@ -141,16 +141,17 @@
> 适用范围:所有前端 UI`static/src` Web 为主;`cli/src` 在视觉可类比处同样适用;以及未来新增的任何界面)。
> 约束级别:写新 UI 或改动 UI 时**必须遵守**;遇到存量违规应在最小改动允许范围内顺手修正。
> 配色基础设施:三模式 CSS 变量定义在 `static/src/styles/base/_tokens.scss``:root[data-theme='classic'|'light'|'dark']`),切换逻辑见 `static/src/utils/theme.ts`
> 配色基础设施:两层 token原始层 + 语义层)定义在 `static/src/styles/base/_tokens.scss``:root[data-theme='classic'|'light'|'dark']` + 首屏回退 `:root:not([data-theme])`),切换逻辑见 `static/src/utils/theme.ts``.stylelintrc.cjs` 三条规则做防回退栏杆,`build` 含 `stylelint` 步骤
1. **禁止边缘光晕**:不使用任何 glow / 外发光 / 彩色光晕效果(大范围彩色 `box-shadow` 扩散、`filter: drop-shadow` 光圈、`::before/::after` 模糊光晕等)。已有的要移除。允许的只是中性、克制的投影——沿用 `--claude-shadow` / `--theme-shadow-*` 既有 token不自造发光阴影。
1. **禁止边缘光晕**:不使用任何 glow / 外发光 / 彩色光晕效果(大范围彩色 `box-shadow` 扩散、`filter: drop-shadow` 光圈、`::before/::after` 模糊光晕等)。已有的要移除。允许的只是中性、克制的投影——沿用 `--shadow-*` 既有 token不自造发光阴影。**仅在用户明确允许或要求时才可使用光晕效果。**
2. **禁止原生浏览器组件**:不直接使用浏览器默认外观的 `<select>`、`<input type=checkbox/radio/range/date/color>`、`alert/confirm/prompt`、原生右键菜单、原生 tooltip 等,一律换成与项目风格统一的自定义组件(下拉 / 开关 / 单选 / 滑块 / 模态 / Toast
3. **禁止圆角套娃**:不允许「圆角矩形套圆角矩形套圆角矩形」的多层嵌套卡片——典型垃圾审美,且极大占据有效显示面积。容器层级要扁平,优先用分隔线 / 留白 / 底色区分,不要多包一层带边框圆角的盒子。
4. **图标外框对齐**:同一组图标按钮的外框(点击热区 / 容器)必须统一对齐,不能一会居中、一会左对齐、一会右对齐;同组固定相同尺寸与对齐方式。
5. **图标视觉对齐而非理论对齐**:按图标视觉重心对齐而非几何边界盒;对重心偏移的图标(三角形 / 播放键 / 放大镜等)做微调,使其「看起来居中」。
6. **颜色遵循三模式切换**:所有颜色走原生三模式(经典 `classic` / 明亮 `light` / 夜间 `dark`),禁止写死颜色字面量,一律用 CSS 变量(`--claude-*` / `--theme-*`)。新增颜色必须在三套主题里都补齐。
6. **颜色遵循三模式切换 + 两层 Token**:所有颜色走原生三模式,禁止写死颜色字面量(裸 hex / `rgb()`/`hsl()` 字面色 / `var(--x, #hex)` 兜底),一律用**中性语义** CSS 变量(`--surface-*` / `--text-*` / `--border-*` / `--accent*` / `--state-*`)。三主题定位:**经典抄 Claude 亮色盘**(暖奶油 + 暖橙 primary `#cc785c`)、**浅色抄 ChatGPT 亮色盘**(冷白 + 灰 + 近黑 primary `#181818`)、**深色自研中性灰阶**。亮色表面靠灰阶拉层次(经典 `--surface-base #faf9f5` < `soft #f5f0e8` < `card #efe9de` < `muted #e8e0d2`不许全塌成白。primary 克制CTA-onlyhover/选中/运行态走中性灰。半透明 tint 用 `color-mix(in srgb, var(--token) N%, transparent)` 派生。`--claude-*`/`--theme-*` 是过时别名,新代码勿用。新增颜色必须三主题(含首屏回退)补齐。
7. **带文字容器固定高度**:所有含内部文字的选项 / 按钮 / 标签 / 容器固定高度,禁止因文字多少被撑大撑高;过长文字用省略号或内部滚动处理。
8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表设置 `max-height` / `max-width`,超出由内部容器滚动,不顶大整个窗口;滚动条要么隐藏,要么做样式适配,不暴露原生粗滚动条。
9. **实体面板禁止半透明**:实体 UI 容器(对话区 / 侧栏 / 下拉/二级菜单 / 抽屉 / 对话框本体 / 状态条 / tooltip / 输入栏)背景必须不透明,且不加 `backdrop-filter` 磨砂。唯一例外:遮罩层 scrim`--overlay-scrim``position:fixed;inset:0`)和刻意玻璃质感装饰保留半透明。
## 6) Git 工作流(开发 + Review

View File

@ -272,7 +272,7 @@ terminal.delete_conversation(conversation_id)
> 适用范围:所有前端 UI`static/src` Web 为主;`cli/src` 在视觉可类比处同样适用;以及未来新增的任何界面)。
> 约束级别:写新 UI 或改动 UI 时**必须遵守**;遇到存量违规应在最小改动允许范围内顺手修正。
1. **禁止边缘光晕**:不使用任何 glow / 外发光 / 彩色光晕效果(大范围彩色 `box-shadow` 扩散、用 `filter: drop-shadow` 做光圈、`::before/::after` 模糊光晕等)。已有的要移除。允许的只是中性、克制的投影——沿用 `--claude-shadow` / `--theme-shadow-*` 这类已有 token不要自造发光阴影。
1. **禁止边缘光晕**:不使用任何 glow / 外发光 / 彩色光晕效果(大范围彩色 `box-shadow` 扩散、用 `filter: drop-shadow` 做光圈、`::before/::after` 模糊光晕等)。已有的要移除。允许的只是中性、克制的投影——沿用 `--claude-shadow` / `--theme-shadow-*` 这类已有 token不要自造发光阴影。**仅在用户明确允许或要求时才可使用光晕效果。**
2. **禁止原生浏览器组件**:不直接使用浏览器默认外观的 `<select>`、`<input type=checkbox/radio/range/date/color>`、`alert/confirm/prompt`、原生右键菜单、原生 tooltip 等。一律替换为与项目风格统一的自定义组件下拉、开关、单选、滑块、模态、Toast 等)。
@ -282,12 +282,21 @@ terminal.delete_conversation(conversation_id)
5. **图标视觉对齐而非理论对齐**:按图标的视觉重心对齐,而不是几何边界盒。对视觉重心偏移的图标(三角形 / 播放键 / 放大镜等做微调micro-nudge让它「看起来居中」而非「数值上居中」。
6. **颜色遵循三模式切换**:所有颜色必须走原生三模式(经典 `classic` / 明亮 `light` / 夜间 `dark`)。禁止写死颜色字面量,一律使用 CSS 变量(`--claude-*` / `--theme-*`,定义在 `static/src/styles/base/_tokens.scss`,由 `:root[data-theme='classic'|'light'|'dark']` 提供;切换逻辑见 `static/src/utils/theme.ts`)。新增颜色必须在三套主题里都补齐。
6. **颜色遵循三模式切换 + 两层 Token**:所有颜色必须走原生三模式(经典 `classic` / 明亮 `light` / 夜间 `dark`),禁止写死颜色字面量(含裸 hex、`rgb()`/`hsl()` 字面色、`var(--x, #hex)` 兜底),一律使用语义 CSS 变量。
- **三主题定位**(填色基准):**经典 = 抄 Claude 官网亮色盘**(暖奶油色阶 + 暖橙 primary `#cc785c`**浅色 = 抄 ChatGPT 亮色盘**(冷白 + 中性灰 + 近黑 primary `#181818`**深色 = 自研中性灰阶**(浅灰/深灰/黑,不硬凑暖调)。
- **两层结构**token 定义在 `static/src/styles/base/_tokens.scss`,分原始层 + 语义层;组件只引用**中性语义名**`--surface-*` / `--text-*` / `--border-*` / `--accent*` / `--state-*` 等)。`--claude-*` / `--theme-*` 是**过时兼容别名**,迁移完成后会删,新代码不要用。
- **表面层次**(经典):画布 `--surface-base` `#faf9f5` < 侧栏 `--surface-soft` `#f5f0e8` < 卡片 `--surface-card` `#efe9de` < 嵌套 `--surface-muted` `#e8e0d2` < 纯白浮起 `--surface-raised`亮色主题不许把所有表面塌成同一个白必须靠暖/冷灰阶拉开层次
- **强调色克制**primary`--accent`)是 "CTA-only voltage",只点在主按钮/logo 等极少数处hover / 选中 / 运行态一律走中性灰(跟各自主题表面色),不抢镜。
- **派生色**:需要半透明 tint 时用 `color-mix(in srgb, var(--token) N%, transparent)` 从语义 token 派生,不写死 rgba。
- **新增颜色**必须在 classic / light / dark 三套主题(及首屏回退 `:root:not([data-theme])`,与经典同值)都补齐。
- **防回退栏杆**`.stylelintrc.cjs` 用 `color-no-hex` / `declaration-property-value-disallowed-list` / `media-feature-name-disallowed-list` 三条规则拦截违规,`build` 脚本含 `stylelint` 步骤会拦下裸色。存量未清理文件在 `BASELINE_EXEMPT` 临时豁免,清理一个移除一个,不再回退。切换逻辑见 `static/src/utils/theme.ts`
7. **带文字容器固定高度**:所有含内部文字的选项 / 按钮 / 标签 / 容器必须固定高度,禁止因文字多少被撑大撑高。文字过长用省略号或内部滚动处理,保持行高与块高稳定。
8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表必须设置 `max-height` / `max-width`,超出时由内部容器滚动,而不是把整个窗口顶大。滚动条要么隐藏,要么做样式适配(贴合整体设计,不暴露原生粗滚动条)。
9. **实体面板禁止半透明**:所有实体 UI 容器(对话区、侧栏、下拉/二级菜单、抽屉、对话框本体、状态条、tooltip 气泡、输入栏等)背景必须**不透明**,且不加 `backdrop-filter` 磨砂。背景一旦不透明,磨砂就失去意义,必须移除。**唯一例外**是遮罩层(弹窗背后压暗的 scrim`--overlay-scrim``position:fixed;inset:0`)和刻意的玻璃质感装饰,这类保留半透明。
## Security Considerations
- 所有用户操作在独立容器中执行,与宿主机隔离

View File

@ -116,7 +116,7 @@ class MainTerminalToolsPolicyMixin:
continue
self.tool_category_states[key] = bool(value)
def apply_personalization_preferences(self, config: Optional[Dict[str, Any]] = None):
def apply_personalization_preferences(self, config: Optional[Dict[str, Any]] = None, *, apply_default_model: bool = True):
"""Apply persisted personalization settings that affect runtime behavior."""
try:
effective_config = config or load_personalization_config(self.data_dir)
@ -176,7 +176,7 @@ class MainTerminalToolsPolicyMixin:
# 默认模型偏好(优先应用,再处理运行模式)
preferred_model = effective_config.get("default_model")
if isinstance(preferred_model, str) and preferred_model != self.model_key:
if apply_default_model and isinstance(preferred_model, str) and preferred_model != self.model_key:
try:
self.set_model(preferred_model)
except Exception as exc:

View File

@ -91,6 +91,7 @@ DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
"deep_compress_behavior": "continue", # 手动压缩行为continue-注入并触发请求 / wait-仅插入等待用户
"silent_tool_disable": True, # 禁用工具时不向模型插入提示(默认开启)
"enhanced_tool_display": True, # 增强工具显示
"compact_message_display": "full", # 简略消息显示full-完整原始内容 / brief-一行概要
"show_git_status_bar": True, # 是否显示输入栏上方 Git 状态栏
"versioning_restore_mode": "overwrite", # 版本回溯模式固定为 overwrite
"agents_md_auto_inject": False, # AGENTS.md 自动注入开关
@ -414,6 +415,13 @@ def sanitize_personalization_payload(
else:
base["enhanced_tool_display"] = bool(base.get("enhanced_tool_display", True))
# 简略消息显示full完整原始内容/ brief一行概要
compact_msg = data.get("compact_message_display", base.get("compact_message_display"))
if isinstance(compact_msg, str) and compact_msg.strip().lower() in ("full", "brief"):
base["compact_message_display"] = compact_msg.strip().lower()
else:
base["compact_message_display"] = "full"
# Git 状态栏显示开关
if "show_git_status_bar" in data:
base["show_git_status_bar"] = bool(data.get("show_git_status_bar"))

2288
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,9 @@
"type": "module",
"scripts": {
"dev": "vite build --watch",
"build": "tsc --noEmit && vite build",
"build": "tsc --noEmit && stylelint \"static/src/**/*.{scss,vue}\" && vite build",
"lint": "eslint \"static/src/**/*.{ts,tsx,js,vue}\" --max-warnings=0",
"lint:css": "stylelint \"static/src/**/*.{scss,vue}\"",
"format": "prettier --write \"static/src/**/*.{ts,tsx,js,vue,css}\"",
"preview": "vite preview",
"cli": "npm --prefix cli run dev",
@ -47,8 +48,11 @@
"@vue/eslint-config-typescript": "^12.0.0",
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.19.2",
"postcss-html": "^1.6.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.1.1",
"sass": "^1.94.2",
"stylelint": "^16.10.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vue-tsc": "^1.8.22"

82
scripts/color_map.py Normal file
View File

@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""
color_map.py 硬编码色值 token 反查
解析 _tokens.scss 各主题真实值对给定色值判定它等于哪个 tokenA类或无对应B类
用法:
python3 scripts/color_map.py <file> # 分析某文件里所有硬编码色的可映射性
"""
import sys, re, os
from collections import defaultdict
TOK = "static/src/styles/base/_tokens.scss"
def norm(v):
"""归一化色值:小写、去多余空格、#abc->#aabbcc、rgb 空格统一。"""
v = v.strip().lower()
v = re.sub(r'\s+', '', v)
m = re.fullmatch(r'#([0-9a-f])([0-9a-f])([0-9a-f])', v)
if m:
v = '#' + ''.join(c*2 for c in m.groups())
return v
def load_tokens():
txt = open(TOK, encoding='utf-8').read()
# semantic blocks only ([data-theme])
themes = {}
for m in re.finditer(r":root\[data-theme='([^']+)'\]\s*\{([^}]*)\}", txt):
name, body = m.group(1), m.group(2)
d = {}
for dm in re.finditer(r'(--[a-z0-9-]+)\s*:\s*([^;]+);', body):
d[norm(dm.group(2))] = dm.group(1)
themes[name] = d
return themes
def analyze(path, themes):
text = open(path, encoding='utf-8').read()
# only style content for vue
if path.endswith('.vue'):
style = '\n'.join(m.group(1) for m in re.finditer(r'<style[^>]*>(.*?)</style>', text, re.S|re.I))
else:
style = text
style = re.sub(r'/\*.*?\*/', '', style, flags=re.S)
style = re.sub(r'//[^\n]*', '', style)
vals = re.findall(r'#[0-9a-fA-F]{3,8}\b|rgba?\([0-9 ,.%/]+\)|hsla?\([0-9 ,.%/]+\)', style)
A = defaultdict(list) # value -> [(theme, token)]
B = defaultdict(int) # value -> count (no match)
counts = defaultdict(int)
for raw in vals:
n = norm(raw)
counts[raw] += 1
hits = []
for tname, d in themes.items():
if n in d:
hits.append((tname, d[n]))
if hits:
A[raw] = hits
else:
B[raw] += 1
return counts, A, B
def main():
themes = load_tokens()
path = sys.argv[1]
counts, A, B = analyze(path, themes)
print(f"=== {path} ===")
print(f"硬编码色值种类: {len(counts)}, 总出现: {sum(counts.values())}")
print(f"\n--- A类(有 token 对应, 可映射) ---")
for v in sorted(A, key=lambda x:-counts[x]):
# 取 classic 优先的 token 名
toks = {t:tok for t,tok in A[v]}
cls = toks.get('classic')
shown = cls or list(toks.values())[0]
themes_hit = ','.join(sorted(toks.keys()))
print(f" {counts[v]:3d}× {v:32s} -> {shown:28s} (命中主题: {themes_hit})")
print(f"\n--- B类(无 token 对应, 野色, 需决策) ---")
for v in sorted(B, key=lambda x:-B[x]):
print(f" {B[v]:3d}× {v}")
if not B:
print(" (无 — 此文件可全量安全迁移)")
if __name__ == '__main__':
main()

143
scripts/lint_check.py Normal file
View File

@ -0,0 +1,143 @@
#!/usr/bin/env python3
"""
lint_check.py Node 段错误环境下的 stylelint 替身验证器
复刻 .stylelintrc.cjs 的三条颜色规则 + 基础语法完整性用于在迁移每个文件后
确认它已彻底清理干净可安全从 BASELINE_EXEMPT 移除
用法:
python3 scripts/lint_check.py <file1> [file2] ...
python3 scripts/lint_check.py --all # 扫所有 .scss/.vue排除永久豁免
规则 stylelintrc 对齐:
R1 color-no-hex : 禁止裸 #hex
R2 literal-rgb-in-color : 颜色属性值禁止 rgb()/hsl() 字面色
R3 var-fallback-in-color : 颜色属性值禁止 var(--x, <fallback>)
R4 prefers-color-scheme : 禁止 @media prefers-color-scheme
注意: 只检查 <style> / scss 内容.vue 文件只扫 <style> 跳过 <template>/<script>
"""
import sys, re, os, glob
PERMANENT_IGNORE = {
'static/src/styles/base/_tokens.scss',
'static/src/styles/components/chat/_virtual-monitor.scss',
}
COLOR_PROP = re.compile(
r'^\s*(-?\w[\w-]*)?\s*('
r'color|fill|stroke|background|background-color|border|border-color|'
r'border-top-color|border-right-color|border-bottom-color|border-left-color|'
r'box-shadow|outline|outline-color|text-decoration-color|caret-color|'
r'column-rule-color|stop-color|text-shadow'
r')\s*:', re.I)
HEX = re.compile(r'#[0-9a-fA-F]{3,8}\b')
RGB = re.compile(r'\b(rgba?|hsla?)\s*\(')
VARFALLBACK = re.compile(r'var\(\s*--[^,()]+,')
def extract_style_blocks(text, is_vue):
"""返回 [(start_line, block_text)]。vue 只取 <style>scss 取全文。"""
if not is_vue:
return [(1, text)]
blocks = []
for m in re.finditer(r'<style[^>]*>(.*?)</style>', text, re.S | re.I):
start_line = text[:m.start(1)].count('\n') + 1
blocks.append((start_line, m.group(1)))
return blocks
def strip_comments(s):
s = re.sub(r'/\*.*?\*/', lambda m: '\n' * m.group(0).count('\n'), s, flags=re.S)
s = re.sub(r'//[^\n]*', '', s)
return s
def check_text(block_text, base_line):
errs = []
clean = strip_comments(block_text)
clean_lines = clean.splitlines()
# R1 hex / R4 prefers-color-scheme —— 行级
for i, line in enumerate(clean_lines):
ln = base_line + i
if 'prefers-color-scheme' in line:
errs.append((ln, 'R4 prefers-color-scheme', line.strip()))
if HEX.search(line):
errs.append((ln, 'R1 no-hex', line.strip()))
# R2/R3 —— 声明级(跨行)。按 ; { } 切成声明段,每段判断是否颜色属性,
# 段内若含 rgb/hsl 字面色或 var(--x, fallback),把违规行号定位到该 token
# 实际出现的那一行声明可能跨多行stylelint 报的是声明起始行;这里
# 我们用 token 出现行,足够定位)。
def line_of(char_idx):
return base_line + clean.count('\n', 0, char_idx)
pos = 0
for m in re.finditer(r'[;{}]', clean):
seg = clean[pos:m.start()]
seg_start = pos
pos = m.end()
if not COLOR_PROP.search(seg):
continue
for vm in VARFALLBACK.finditer(seg):
errs.append((line_of(seg_start + vm.start()), 'R3 var-fallback', seg.strip()[:80]))
for rm in RGB.finditer(seg):
errs.append((line_of(seg_start + rm.start()), 'R2 literal-rgb', seg.strip()[:80]))
# 处理最后一段(无尾分隔符的残留,通常为空)
seg = clean[pos:]
if COLOR_PROP.search(seg):
for vm in VARFALLBACK.finditer(seg):
errs.append((line_of(pos + vm.start()), 'R3 var-fallback', seg.strip()[:80]))
for rm in RGB.finditer(seg):
errs.append((line_of(pos + rm.start()), 'R2 literal-rgb', seg.strip()[:80]))
errs.sort(key=lambda e: e[0])
return errs
def syntax_check(text, is_vue):
"""大括号配对(针对 style 内容)。"""
blocks = extract_style_blocks(text, is_vue)
problems = []
for base, bt in blocks:
bt2 = strip_comments(bt)
if bt2.count('{') != bt2.count('}'):
problems.append(f" 大括号不配对 (block@L{base}): {{={bt2.count('{')} }}={bt2.count('}')}")
return problems
def lint_file(path):
text = open(path, encoding='utf-8').read()
is_vue = path.endswith('.vue')
all_errs = []
for base, bt in extract_style_blocks(text, is_vue):
all_errs.extend(check_text(bt, base))
syn = syntax_check(text, is_vue)
return all_errs, syn
def main():
args = sys.argv[1:]
if not args:
print("用法: python3 scripts/lint_check.py <file...> | --all")
sys.exit(2)
if args == ['--all']:
files = []
for pat in ('static/src/**/*.scss', 'static/src/**/*.vue'):
files += glob.glob(pat, recursive=True)
files = [f for f in files if f.replace('\\', '/') not in PERMANENT_IGNORE]
else:
files = args
total = 0
for f in sorted(files):
nf = f.replace('\\', '/')
if nf in PERMANENT_IGNORE:
print(f"SKIP (permanent ignore): {f}")
continue
errs, syn = lint_file(f)
if errs or syn:
print(f"\n{f}")
for ln, rule, txt in errs:
print(f" L{ln:<5} {rule:22} | {txt[:80]}")
for s in syn:
print(s)
total += len(errs) + len(syn)
else:
print(f"{f}")
print(f"\n总问题数: {total}")
sys.exit(1 if total else 0)
if __name__ == '__main__':
main()

120
scripts/strip_fallback.py Normal file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""
strip_fallback.py 安全剥离 var(--DEFINED_TOKEN, fallback) fallback
仅当 token _tokens.scss 中已定义时才剥离此时 fallback 永不生效剥离=零视觉变化
未定义 tokenfallback 是真实值一律跳过
--write 才落盘否则只 dry-run 报告
安全校验每个被改的位置改动后只是去掉了 ", fallback"token 名和其余内容不变
"""
import sys, re, glob, os
def load_defined():
txt = open('static/src/styles/base/_tokens.scss', encoding='utf-8').read()
return set(re.findall(r'(--[a-z0-9-]+)\s*:', txt))
def find_var_spans(s):
"""返回所有 var(...) 的 (start, end, inner) 支持嵌套end 是右括号下一位。"""
spans = []
i = 0
while True:
m = re.search(r'var\(', s[i:])
if not m:
break
start = i + m.start()
# 从 'var(' 的左括号开始做括号匹配
depth = 0
j = start + 3 # points at '('
assert s[j] == '('
k = j
while k < len(s):
if s[k] == '(':
depth += 1
elif s[k] == ')':
depth -= 1
if depth == 0:
break
k += 1
inner = s[j+1:k]
spans.append((start, k+1, inner))
i = k + 1
return spans
def strip_one(s, defined):
"""对字符串 s 处理最外层 var(),递归处理 inner。返回 (新串, 改动数)。"""
spans = find_var_spans(s)
if not spans:
return s, 0
out = []
last = 0
changes = 0
for start, end, inner in spans:
out.append(s[last:start])
# 先递归处理 inner处理嵌套 var
new_inner, c_inner = strip_one(inner, defined)
changes += c_inner
# 解析最外层 var(--tok, fallback?) —— 在顶层逗号处分割
mtok = re.match(r'\s*(--[a-z0-9-]+)\s*', new_inner)
if mtok:
tok = mtok.group(1)
rest = new_inner[mtok.end():]
if rest.startswith(','):
# 有 fallback。token 已定义 → 剥离
if tok in defined:
out.append(f'var({tok})')
changes += 1
else:
out.append(f'var({new_inner})')
else:
out.append(f'var({new_inner})')
else:
out.append(f'var({new_inner})')
last = end
out.append(s[last:])
return ''.join(out), changes
def process_file(path, defined, write):
src = open(path, encoding='utf-8').read()
new, ch = strip_one(src, defined)
if ch and write:
open(path, 'w', encoding='utf-8').write(new)
return ch, src, new
def verify(src, new):
"""校验去掉所有空白后new 必须是 src 删除若干 ', fallback' 的结果。
粗校验new 的非空白字符是 src 非空白字符的子序列且行数不变"""
if src.count('\n') != new.count('\n'):
return False, "行数变化"
# 每行new 行必须是 old 行的子序列(只删不增)
for o, n in zip(src.splitlines(), new.splitlines()):
oi = 0
for ch in n:
oi = o.find(ch, oi)
if oi == -1:
return False, f"非子序列: {o!r} -> {n!r}"
oi += 1
return True, "ok"
def main():
write = '--write' in sys.argv
args = [a for a in sys.argv[1:] if not a.startswith('--')]
defined = load_defined()
files = args or (glob.glob('static/src/**/*.scss', recursive=True) +
glob.glob('static/src/**/*.vue', recursive=True))
files = [f for f in files if '_tokens' not in f and '_virtual-monitor' not in f]
total = 0
for f in sorted(files):
ch, src, new = process_file(f, defined, write=False)
if not ch:
continue
ok, msg = verify(src, new)
flag = "OK" if ok else f"FAIL({msg})"
print(f"{ch:3d} strips [{flag}] {f}")
if ok and write:
open(f, 'w', encoding='utf-8').write(new)
total += ch
print(f"\n{'已写入' if write else 'DRY-RUN'} 总剥离: {total}")
if __name__ == '__main__':
main()

View File

@ -67,7 +67,29 @@ def _apply_workspace_personalization_preferences(terminal: WebTerminal, workspac
"""Apply persisted workspace personalization after policy/workspace resolution."""
try:
config = load_personalization_config(workspace.data_dir)
terminal.apply_personalization_preferences(config)
session_model = None
if has_request_context():
raw_session_model = session.get("model_key")
if isinstance(raw_session_model, str) and raw_session_model.strip():
session_model = raw_session_model.strip()
# default_model 是“新会话初始偏好”,不能在每次 /api/status、任务创建、
# 加载资源时覆盖用户已经在当前会话里手动切换的模型。
if session_model and session_model != getattr(terminal, "model_key", None):
try:
terminal.set_model(session_model)
except Exception as exc:
debug_log(f"[Personalization] 恢复会话模型失败: {session_model} ({exc})")
apply_default_model = not bool(session_model) and not bool(
getattr(terminal, "_workspace_default_model_applied", False)
)
terminal.apply_personalization_preferences(config, apply_default_model=apply_default_model)
if apply_default_model:
try:
terminal._workspace_default_model_applied = True
except Exception:
pass
if has_request_context():
session["run_mode"] = getattr(terminal, "run_mode", session.get("run_mode"))
session["thinking_mode"] = getattr(terminal, "thinking_mode", session.get("thinking_mode"))

View File

@ -123,6 +123,7 @@
:class="{
'chat-container--immersive': workspaceCollapsed,
'chat-container--mobile': isMobileViewport,
'chat-container--app-shell': isAppShell,
'chat-container--monitor': chatDisplayMode === 'monitor',
'has-title-ribbon': titleRibbonVisible
}"
@ -370,11 +371,6 @@
@reset-context="resetGitChangesContext"
/>
</transition>
<div
v-if="!isMobileViewport && rightCollapsed && !gitChangesPanelOpen"
class="resize-handle"
@mousedown="startResize('right', $event)"
></div>
</div>
<transition name="desktop-approval-fade">
<div v-if="!isMobileViewport && !rightCollapsed" class="desktop-approval-overlay">
@ -818,7 +814,7 @@
</template>
<script setup lang="ts">
import { defineAsyncComponent } from 'vue';
import { defineAsyncComponent, onMounted, ref } from 'vue';
import appOptions from './app';
import VideoPicker from './components/overlay/VideoPicker.vue';
import { useTutorialStore } from './stores/tutorial';
@ -832,10 +828,23 @@ const PathAuthorizationDialog = defineAsyncComponent(
const tutorialStore = useTutorialStore();
const mobilePanelIcon = new URL('../icons/align-left.svg', import.meta.url).href;
const isAppShell =
typeof window !== 'undefined' &&
(new URLSearchParams(window.location.search).has('app_shell') ||
Boolean((window as any)?.AndroidThemeBridge));
const detectAppShell = () => {
if (typeof window === 'undefined') return false;
const params = new URLSearchParams(window.location.search);
const ua = window.navigator?.userAgent || '';
return (
params.has('app_shell') ||
Boolean((window as any)?.AndroidThemeBridge) ||
/;\s*wv\)/i.test(ua)
);
};
const isAppShell = ref(detectAppShell());
onMounted(() => {
isAppShell.value = detectAppShell();
window.setTimeout(() => {
isAppShell.value = detectAppShell();
}, 300);
});
const mobileMenuIcons = {
workspace: new URL('../icons/folder.svg', import.meta.url).href,
personal: new URL('../icons/user.svg', import.meta.url).href,

View File

@ -651,7 +651,7 @@ button {
border: none;
border-radius: 12px;
padding: 10px 14px;
background: var(--claude-highlight, #f3d2a8);
background: var(--claude-highlight);
color: #2a2013;
font-weight: 600;
cursor: pointer;

View File

@ -612,7 +612,7 @@ watch(secondaryVerified, async (val) => {
color: var(--muted);
}
.status.error {
color: #dc2626;
color: var(--state-danger);
}
.editor-only {
max-width: 1080px;
@ -708,7 +708,7 @@ watch(secondaryVerified, async (val) => {
color: var(--muted);
}
.error {
color: #dc2626;
color: var(--state-danger);
}
.empty {
color: var(--muted);
@ -729,8 +729,8 @@ watch(secondaryVerified, async (val) => {
.primary {
background: linear-gradient(
135deg,
var(--claude-accent, #da7756) 0%,
var(--claude-accent-strong, #bd5d3a) 100%
var(--claude-accent) 0%,
var(--claude-accent-strong) 100%
);
color: #fff;
border: none;

View File

@ -54,8 +54,8 @@ onMounted(() => {
--border: #e3d8c8;
--ink: #1a120b;
--muted: #6f635a;
--accent: var(--claude-accent, #da7756);
--accent-strong: var(--claude-accent-strong, #bd5d3a);
--accent: var(--claude-accent);
--accent-strong: var(--claude-accent-strong);
--shadow: 0 16px 40px rgba(47, 32, 21, 0.12);
max-width: 1080px;
@ -114,7 +114,7 @@ onMounted(() => {
color: var(--muted);
}
.status.error {
color: #d64545;
color: var(--state-danger);
}
.markdown :global(h1),
.markdown :global(h2),

View File

@ -1367,8 +1367,8 @@ button:disabled {
gap: 12px;
padding: 12px 14px;
border-radius: 14px;
border: 1px solid var(--theme-control-border, rgba(118, 103, 84, 0.25));
background: var(--theme-surface-muted, rgba(255, 255, 255, 0.85));
border: 1px solid var(--theme-control-border);
background: var(--theme-surface-muted);
cursor: pointer;
transition:
border-color 0.2s ease,
@ -1381,8 +1381,8 @@ button:disabled {
}
.toggle-row:hover {
border-color: var(--theme-control-border-strong, rgba(118, 103, 84, 0.35));
background: var(--theme-surface-soft, rgba(255, 255, 255, 0.92));
border-color: var(--theme-control-border-strong);
background: var(--theme-surface-soft);
}
.toggle-row input {
@ -1410,7 +1410,7 @@ button:disabled {
.fancy-path {
fill: none;
stroke: var(--claude-text-secondary, #7f7766);
stroke: var(--claude-text-secondary);
stroke-width: 5;
stroke-linecap: round;
stroke-linejoin: round;
@ -1423,13 +1423,13 @@ button:disabled {
}
.toggle-row input:checked + .fancy-check .fancy-path {
stroke: var(--claude-accent, #da7756);
stroke: var(--claude-accent);
stroke-dasharray: 70.5096664428711 9999999;
stroke-dashoffset: -262.2723388671875;
}
.toggle-row span {
color: var(--claude-text, #3d3929);
color: var(--claude-text);
font-weight: 600;
}

View File

@ -139,7 +139,7 @@ export const computed = {
return !this.isMobileViewport && this.chatDisplayMode === 'chat';
},
chatContainerStyle() {
const minHeight = 80;
const minHeight = 90;
const raw = Number(this.composerReservedHeight || 0);
const height = Number.isFinite(raw) ? Math.max(minHeight, Math.round(raw)) : minHeight;
const growth = Math.max(0, height - minHeight);

View File

@ -1191,6 +1191,7 @@ export const uiMethods = {
}
this.messages = [];
this.currentConversationId = null;
this.resetTokenStatistics?.();
this.currentConversationTitle = '新对话';
this.titleReady = true;
this.suppressTitleTyping = false;

View File

@ -28,8 +28,8 @@ body {
padding: 30px;
border-radius: 18px;
background: var(--theme-surface-soft);
border: 1px solid var(--theme-control-border, var(--claude-border));
box-shadow: var(--theme-shadow-mid, var(--claude-shadow));
border: 1px solid var(--theme-control-border);
box-shadow: var(--theme-shadow-mid);
backdrop-filter: blur(8px);
}
@ -61,7 +61,7 @@ body {
min-height: 46px;
box-sizing: border-box;
border-radius: 12px;
border: 1px solid var(--theme-control-border, var(--claude-border));
border: 1px solid var(--theme-control-border);
background: var(--theme-surface-strong);
color: var(--claude-text);
padding: 11px 12px;
@ -86,7 +86,7 @@ body {
font-size: 0.95rem;
cursor: pointer;
background: linear-gradient(135deg, var(--claude-accent), var(--claude-accent-strong));
color: #fff;
color: var(--on-accent);
transition:
transform 0.15s ease,
filter 0.15s ease;
@ -117,7 +117,7 @@ body {
.auth-error {
min-height: 1.2em;
margin-top: 12px;
color: #e85b57;
color: var(--state-danger);
font-size: 0.86rem;
}

View File

@ -634,7 +634,11 @@ const blockDisplayMode = computed(() => {
return personalization.experiments.blockDisplayMode || 'stacked';
});
const compactMessageDisplay = computed(() => {
return personalization.experiments.compactMessageDisplay || 'full';
return (
personalization.form.compact_message_display ||
personalization.experiments.compactMessageDisplay ||
'full'
);
});
const stackedBlocksEnabled = computed(() => {
// 使
@ -790,6 +794,7 @@ const {
});
const rootEl = scrollRef;
const thinkingRefs = new Map<string, HTMLElement | null>();
let scrollbarResizeObserver: ResizeObserver | null = null;
let bounceTraceCount = 0;
const BOUNCE_TRACE_MAX = 240;
const bounceTraceLastTsByKey = new Map<string, number>();
@ -1005,6 +1010,13 @@ function attachBounceListener() {
}
}
function updateChatScrollbarWidth() {
const el = scrollRef.value;
if (!el) return;
const scrollbarWidth = Math.max(0, el.offsetWidth - el.clientWidth);
el.style.setProperty('--chat-scrollbar-width', `${scrollbarWidth}px`);
}
watch(
[isAtBottom, isNearBottom, escapedFromLock],
([atBottom, nearBottom, escaped]) => {
@ -1459,6 +1471,11 @@ function compactBriefLabel(msg: any): string {
onMounted(() => {
attachBounceListener();
updateChatScrollbarWidth();
if (typeof ResizeObserver !== 'undefined' && scrollRef.value) {
scrollbarResizeObserver = new ResizeObserver(updateChatScrollbarWidth);
scrollbarResizeObserver.observe(scrollRef.value);
}
console.warn('[SCROLL_BOUNCE_TRACE]', 'mounted', {
hasScrollRef: !!scrollRef.value
});
@ -1469,10 +1486,23 @@ onMounted(() => {
watch(scrollRef, () => {
attachBounceListener();
updateChatScrollbarWidth();
if (scrollbarResizeObserver) {
scrollbarResizeObserver.disconnect();
scrollbarResizeObserver = null;
}
if (typeof ResizeObserver !== 'undefined' && scrollRef.value) {
scrollbarResizeObserver = new ResizeObserver(updateChatScrollbarWidth);
scrollbarResizeObserver.observe(scrollRef.value);
}
});
onBeforeUnmount(() => {
detachBounceListener();
if (scrollbarResizeObserver) {
scrollbarResizeObserver.disconnect();
scrollbarResizeObserver = null;
}
if (timerHandle !== null) {
clearInterval(timerHandle);
timerHandle = null;

View File

@ -541,12 +541,15 @@ watch(
<style scoped>
.minimal-blocks-container {
--chat-content-x: 0px;
margin: 16px 0;
width: 100%;
}
/* 摘要组 */
.summary-group {
margin: 16px 0;
width: 100%;
}
.sub-agent-system-group {
@ -567,7 +570,8 @@ watch(
grid-template-columns: minmax(0, 1fr) auto;
align-items: start;
column-gap: 8px;
padding: 0 6px 0 6px;
width: 100%;
padding: 0;
cursor: pointer;
transition: background-color 0.2s;
}
@ -633,14 +637,15 @@ watch(
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s cubic-bezier(0.4, 0, 0.2, 1);
margin-top: 8px;
padding: 0 6px 0 6px;
width: 100%;
padding: 0;
}
/* 桌面端与正式文本输出左边距保持一致text-content: 0 20px 0 15px */
/* 桌面端与正式文本输出左右内边距保持一致 */
@media (min-width: 769px) {
.summary-line-text,
.steps-container {
padding: 0 20px 0 15px;
padding: 0 var(--chat-content-x);
}
}
@ -726,6 +731,28 @@ watch(
display: block;
/* 让第一行文字中心和 SVG 中心对齐:向上移动半个行高减去半个字高 */
margin-top: calc((1em - 1.7em) / 2);
/* 滚动条样式(参考 git 状态侧边栏文件列表,滚动槽透明贴合灰色底色) */
scrollbar-width: thin; /* Firefox */
scrollbar-color: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent)
transparent;
}
.thinking-content::-webkit-scrollbar {
width: 10px;
height: 10px;
}
.thinking-content::-webkit-scrollbar-track {
background: transparent;
border-radius: 999px;
margin: 8px;
}
.thinking-content::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent);
border-radius: 999px;
border: 2px solid transparent;
background-clip: padding-box;
}
/* 工具内容 */
@ -802,7 +829,7 @@ watch(
@media (max-width: 768px) {
.text-output {
margin: 16px -9px;
margin: 16px 0;
}
}

View File

@ -1089,7 +1089,7 @@ function renderEasterEgg(result: any, args: any): string {
.tool-result-meta {
margin-bottom: 12px;
padding: 8px 12px;
background: rgba(0, 0, 0, 0.02);
background: var(--hover-bg);
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
@ -1106,10 +1106,47 @@ function renderEasterEgg(result: any, args: any): string {
.tool-result-content.scrollable {
max-height: 400px;
overflow-y: auto;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid var(--border-default);
border-radius: 6px;
padding: 8px;
background: rgba(0, 0, 0, 0.01);
background: color-mix(in srgb, transparent 99%, var(--text-primary));
}
/* 结构化显示滚动条样式(参考 git 状态侧边栏文件列表,滚动槽透明贴合灰色底色) */
.tool-result-content.scrollable,
.tool-result-diff.scrollable,
.code-block pre,
.output-block pre {
scrollbar-width: thin; /* Firefox */
scrollbar-color: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent)
transparent;
}
.tool-result-content.scrollable::-webkit-scrollbar,
.tool-result-diff.scrollable::-webkit-scrollbar,
.code-block pre::-webkit-scrollbar,
.output-block pre::-webkit-scrollbar {
width: 10px;
height: 10px;
}
.tool-result-content.scrollable::-webkit-scrollbar-track,
.tool-result-diff.scrollable::-webkit-scrollbar-track,
.code-block pre::-webkit-scrollbar-track,
.output-block pre::-webkit-scrollbar-track {
background: transparent;
border-radius: 999px;
margin: 8px;
}
.tool-result-content.scrollable::-webkit-scrollbar-thumb,
.tool-result-diff.scrollable::-webkit-scrollbar-thumb,
.code-block pre::-webkit-scrollbar-thumb,
.output-block pre::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent);
border-radius: 999px;
border: 2px solid transparent;
background-clip: padding-box;
}
.tool-result-content pre {
@ -1128,7 +1165,7 @@ function renderEasterEgg(result: any, args: any): string {
}
.tool-result-empty {
color: rgba(0, 0, 0, 0.5);
color: var(--text-secondary);
font-style: italic;
padding: 8px;
}
@ -1141,15 +1178,15 @@ function renderEasterEgg(result: any, args: any): string {
.search-result-item {
padding: 10px;
margin-bottom: 8px;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid var(--border-default);
border-radius: 6px;
background: rgba(0, 0, 0, 0.01);
background: color-mix(in srgb, transparent 99%, var(--text-primary));
}
/* 暗色模式下的搜索结果 */
:root[data-theme='dark'] .search-result-item {
background: #2a2a2a;
border: 1px solid rgba(255, 255, 255, 0.08);
background: var(--badge-bg);
border: 1px solid var(--border-default);
}
.search-result-title {
@ -1160,20 +1197,20 @@ function renderEasterEgg(result: any, args: any): string {
.search-result-url {
font-size: 12px;
color: rgba(0, 0, 0, 0.6);
color: var(--text-secondary);
}
:root[data-theme='dark'] .search-result-url {
color: rgba(255, 255, 255, 0.6);
color: var(--text-secondary);
}
.search-result-url a {
color: #0066cc;
color: var(--state-info);
text-decoration: none;
}
:root[data-theme='dark'] .search-result-url a {
color: #6b9fff;
color: var(--state-info);
}
.search-result-url a:hover {
@ -1191,10 +1228,10 @@ function renderEasterEgg(result: any, args: any): string {
.tool-result-diff.scrollable {
max-height: 400px;
overflow-y: auto;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid var(--border-default);
border-radius: 6px;
padding: 8px;
background: rgba(0, 0, 0, 0.01);
background: color-mix(in srgb, transparent 99%, var(--text-primary));
}
.diff-line {
@ -1225,13 +1262,13 @@ function renderEasterEgg(result: any, args: any): string {
}
.diff-remove {
background: rgba(255, 0, 0, 0.1);
color: #c00;
background: var(--diff-del-bg);
color: var(--state-danger);
}
.diff-add {
background: rgba(0, 255, 0, 0.1);
color: #0a0;
background: var(--diff-add-bg);
color: var(--state-success);
}
.diff-separator {
@ -1249,7 +1286,7 @@ function renderEasterEgg(result: any, args: any): string {
.diff-operation {
font-weight: 600;
margin: 8px 0 4px;
color: rgba(0, 0, 0, 0.7);
color: var(--text-secondary);
}
/* 图片 */
@ -1262,7 +1299,7 @@ function renderEasterEgg(result: any, args: any): string {
max-width: 100%;
height: auto;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid var(--border-default);
}
/* 代码块 */
@ -1281,8 +1318,8 @@ function renderEasterEgg(result: any, args: any): string {
.output-block pre {
margin: 0;
padding: 12px;
background: rgba(0, 0, 0, 0.03);
border: 1px solid rgba(0, 0, 0, 0.1);
background: var(--hover-bg);
border: 1px solid var(--border-default);
border-radius: 6px;
overflow-x: auto;
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
@ -1295,17 +1332,17 @@ function renderEasterEgg(result: any, args: any): string {
/* 暗色模式run_python 代码/输出统一白字,去除任何描边/阴影观感 */
:root[data-theme='dark'] .code-block pre,
:root[data-theme='dark'] .output-block pre {
color: #ffffff !important;
background: rgba(255, 255, 255, 0.04);
border-color: rgba(255, 255, 255, 0.12);
color: var(--text-primary) !important;
background: var(--hover-bg);
border-color: var(--border-strong);
text-shadow: none !important;
}
:root[data-theme='dark'] .code-block pre code,
:root[data-theme='dark'] .output-block pre code {
color: #ffffff !important;
color: var(--text-primary) !important;
text-shadow: none !important;
-webkit-text-fill-color: #ffffff;
-webkit-text-fill-color: var(--text-primary);
}
.output-block {
@ -1326,18 +1363,18 @@ function renderEasterEgg(result: any, args: any): string {
}
.memory-add {
background: rgba(0, 255, 0, 0.1);
color: #0a0;
background: var(--diff-add-bg);
color: var(--state-success);
}
.memory-update {
background: rgba(255, 165, 0, 0.1);
color: #f90;
background: color-mix(in srgb, var(--state-warning) 10%, transparent);
color: var(--state-warning);
}
.memory-delete {
background: rgba(255, 0, 0, 0.1);
color: #c00;
background: var(--diff-del-bg);
color: var(--state-danger);
}
/* 待办事项 */
@ -1361,8 +1398,8 @@ function renderEasterEgg(result: any, args: any): string {
/* 彩蛋 */
.easter-egg-content {
padding: 12px;
background: linear-gradient(135deg, rgba(255, 215, 0, 0.1), rgba(255, 105, 180, 0.1));
border: 1px solid rgba(255, 215, 0, 0.3);
background: linear-gradient(135deg, color-mix(in srgb, var(--decorative-gold) 10%, transparent), color-mix(in srgb, var(--decorative-pink) 10%, transparent));
border: 1px solid color-mix(in srgb, var(--decorative-gold) 30%, transparent);
border-radius: 8px;
font-size: 14px;
line-height: 1.6;
@ -1371,7 +1408,7 @@ function renderEasterEgg(result: any, args: any): string {
/* 搜索匹配项 */
.search-match-separator {
text-align: center;
color: rgba(0, 0, 0, 0.3);
color: var(--text-tertiary);
margin: 12px 0;
font-size: 16px;
}
@ -1379,23 +1416,23 @@ function renderEasterEgg(result: any, args: any): string {
.search-match-item {
margin-bottom: 12px;
padding: 8px;
background: rgba(0, 0, 0, 0.02);
background: var(--hover-bg);
border-radius: 6px;
}
:root[data-theme='dark'] .search-match-item {
background: rgba(255, 255, 255, 0.05);
background: var(--hover-bg);
}
.search-match-line {
font-size: 11px;
color: rgba(0, 0, 0, 0.5);
color: var(--text-secondary);
margin-bottom: 4px;
font-weight: 600;
}
:root[data-theme='dark'] .search-match-line {
color: rgba(255, 255, 255, 0.5);
color: var(--text-secondary);
}
.search-match-item pre {
@ -1407,7 +1444,7 @@ function renderEasterEgg(result: any, args: any): string {
/* 提取片段 */
.segment-separator {
text-align: center;
color: rgba(0, 0, 0, 0.3);
color: var(--text-tertiary);
margin: 12px 0;
font-size: 16px;
}
@ -1415,23 +1452,23 @@ function renderEasterEgg(result: any, args: any): string {
.segment-item {
margin-bottom: 12px;
padding: 8px;
background: rgba(0, 0, 0, 0.02);
background: var(--hover-bg);
border-radius: 6px;
}
:root[data-theme='dark'] .segment-item {
background: rgba(255, 255, 255, 0.05);
background: var(--hover-bg);
}
.segment-range {
font-size: 11px;
color: rgba(0, 0, 0, 0.5);
color: var(--text-secondary);
margin-bottom: 4px;
font-weight: 600;
}
:root[data-theme='dark'] .segment-range {
color: rgba(255, 255, 255, 0.5);
color: var(--text-secondary);
}
.segment-item pre {
@ -1443,7 +1480,7 @@ function renderEasterEgg(result: any, args: any): string {
/* 个性化管理 */
.personalization-message {
padding: 12px;
background: rgba(0, 0, 0, 0.02);
background: var(--hover-bg);
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
@ -1451,17 +1488,17 @@ function renderEasterEgg(result: any, args: any): string {
}
:root[data-theme='dark'] .personalization-message {
background: rgba(255, 255, 255, 0.05);
background: var(--hover-bg);
}
.personalization-config-title {
font-weight: 600;
margin-bottom: 8px;
color: rgba(0, 0, 0, 0.8);
color: var(--text-primary);
}
:root[data-theme='dark'] .personalization-config-title {
color: rgba(255, 255, 255, 0.9);
color: var(--text-primary);
}
.personalization-config-list {
@ -1474,30 +1511,30 @@ function renderEasterEgg(result: any, args: any): string {
justify-content: space-between;
align-items: center;
padding: 6px 10px;
background: rgba(0, 0, 0, 0.02);
background: var(--hover-bg);
border-radius: 4px;
font-size: 12px;
}
:root[data-theme='dark'] .config-item {
background: rgba(255, 255, 255, 0.05);
background: var(--hover-bg);
}
.config-label {
color: rgba(0, 0, 0, 0.6);
color: var(--text-secondary);
font-weight: 500;
}
:root[data-theme='dark'] .config-label {
color: rgba(255, 255, 255, 0.6);
color: var(--text-secondary);
}
.config-value {
color: rgba(0, 0, 0, 0.9);
color: var(--text-primary);
font-weight: 400;
}
:root[data-theme='dark'] .config-value {
color: rgba(255, 255, 255, 0.9);
color: var(--text-primary);
}
</style>

View File

@ -130,8 +130,8 @@
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>
<span class="floating-project-status__add">+<RollingNumber :value="projectGitSummaryForRender.additions" /></span>
<span class="floating-project-status__del">-<RollingNumber :value="projectGitSummaryForRender.deletions" /></span>
</button>
</span>
</div>
@ -185,15 +185,6 @@
</div>
</div>
<div class="input-row">
<button
type="button"
class="stadium-btn add-btn"
data-tutorial="quick-menu-open"
@click.stop="$emit('toggle-quick-menu')"
:disabled="!isConnected"
>
+
</button>
<div class="stadium-input-rich">
<EditorContent
:editor="editor"
@ -203,6 +194,17 @@
@focusout="onInputBlur"
/>
</div>
</div>
<div class="input-actions">
<button
type="button"
class="stadium-btn add-btn"
data-tutorial="quick-menu-open"
@click.stop="$emit('toggle-quick-menu')"
:disabled="!isConnected"
>
+
</button>
<button
type="button"
class="stadium-btn send-btn"
@ -366,6 +368,7 @@ import Mention from '@tiptap/extension-mention';
import Placeholder from '@tiptap/extension-placeholder';
import { TextSelection } from 'prosemirror-state';
import QuickMenu from '@/components/input/QuickMenu.vue';
import RollingNumber from '@/components/input/RollingNumber.vue';
import { useInputStore } from '@/stores/input';
import { usePersonalizationStore } from '@/stores/personalization';
@ -1512,7 +1515,7 @@ const emitComposerHeight = () => {
}
const baseline = baselineComposerVisualHeight.value || visualHeight;
const growth = Math.max(0, visualHeight - baseline);
const baseReservedHeight = 80;
const baseReservedHeight = 90;
const reservedHeight = Math.ceil(baseReservedHeight + growth);
emit('composer-height-change', {
reservedHeight,
@ -1527,6 +1530,9 @@ const adjustTextareaSize = () => {
return;
}
const target = root;
const previousScrollTop = target.scrollTop;
const selectionShouldStayVisible =
document.activeElement === target || target.contains(document.activeElement);
target.style.height = 'auto';
const computedStyle = window.getComputedStyle(target);
const lineHeight = parseFloat(computedStyle.lineHeight || '20') || 20;
@ -1536,11 +1542,58 @@ const adjustTextareaSize = () => {
const multiline = targetHeight > lineHeight * 1.4;
applyLineMetrics(lines, multiline);
target.style.height = `${targetHeight}px`;
keepEditorSelectionVisible(target, {
previousScrollTop,
selectionShouldStayVisible,
padding: Math.max(4, lineHeight * 0.3)
});
nextTick(() => {
emitComposerHeight();
});
};
const keepEditorSelectionVisible = (
target: HTMLElement,
options: { previousScrollTop: number; selectionShouldStayVisible: boolean; padding: number }
) => {
const maxScrollTop = Math.max(0, target.scrollHeight - target.clientHeight);
if (maxScrollTop <= 0) {
target.scrollTop = 0;
return;
}
if (!options.selectionShouldStayVisible) {
target.scrollTop = Math.min(maxScrollTop, Math.max(0, options.previousScrollTop));
return;
}
requestAnimationFrame(() => {
const instance = editor.value;
if (!instance) {
target.scrollTop = Math.min(maxScrollTop, Math.max(0, options.previousScrollTop));
return;
}
try {
const caretRect = instance.view.coordsAtPos(instance.state.selection.head);
const targetRect = target.getBoundingClientRect();
const padding = options.padding;
const currentMaxScrollTop = Math.max(0, target.scrollHeight - target.clientHeight);
let nextScrollTop = target.scrollTop;
if (caretRect.bottom > targetRect.bottom - padding) {
nextScrollTop += caretRect.bottom - targetRect.bottom + padding;
} else if (caretRect.top < targetRect.top + padding) {
nextScrollTop -= targetRect.top + padding - caretRect.top;
}
target.scrollTop = Math.min(currentMaxScrollTop, Math.max(0, nextScrollTop));
} catch {
target.scrollTop = Math.min(maxScrollTop, Math.max(0, options.previousScrollTop));
}
});
};
const onInput = (event: Event) => {
const target = event.target as HTMLTextAreaElement;
if (!props.isConnected || props.inputLocked) {
@ -1638,9 +1691,9 @@ const contextUsagePercentLabel = computed(() => `${Math.round(contextUsagePercen
const contextUsageColor = computed(() => {
const percent = contextUsagePercent.value;
if (percent < 40) return '#16a34a';
if (percent <= 80) return '#f59e0b';
return '#ef4444';
if (percent < 40) return 'var(--state-success)';
if (percent <= 80) return 'var(--state-warning)';
return 'var(--state-danger)';
});
const formatCompactTokens = (value: number) => {
@ -1815,7 +1868,7 @@ onBeforeUnmount(() => {
<style scoped>
.floating-project-status {
position: absolute;
left: calc(5% - 7px);
left: 5%;
bottom: calc(100% + 6px);
z-index: auto;
width: 90%;
@ -1828,7 +1881,7 @@ onBeforeUnmount(() => {
padding: 4px 10px 4px 4px;
border: 1px solid var(--claude-border);
border-radius: 14px;
background: var(--theme-surface-muted);
background: var(--theme-surface-soft);
box-shadow: none;
pointer-events: auto;
}
@ -1884,8 +1937,8 @@ onBeforeUnmount(() => {
.floating-project-status__button--active,
.floating-project-status__notice--button:hover,
.floating-project-status__submenu button:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
box-shadow: 0 1px 2px rgba(61, 57, 41, 0.08);
background: var(--theme-tab-active);
box-shadow: 0 1px 2px var(--shadow-color);
}
.floating-project-status__button--project {
@ -1925,15 +1978,19 @@ onBeforeUnmount(() => {
}
.floating-project-status__stats:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
box-shadow: 0 1px 2px rgba(61, 57, 41, 0.08);
background: var(--theme-tab-active);
box-shadow: 0 1px 2px var(--shadow-color);
}
.floating-project-status__add {
display: inline-flex;
align-items: center;
color: var(--git-diff-add-text);
}
.floating-project-status__del {
display: inline-flex;
align-items: center;
color: var(--git-diff-del-text);
}
@ -1951,7 +2008,7 @@ onBeforeUnmount(() => {
padding: 6px;
border: 1px solid var(--claude-border);
border-radius: 14px;
background: var(--theme-surface-muted);
background: var(--theme-surface-soft);
box-shadow: none;
overflow: hidden;
}
@ -1978,8 +2035,8 @@ onBeforeUnmount(() => {
:global(body[data-theme='dark'] .floating-project-status),
:global(html[data-theme='dark'] .floating-project-status__submenu),
:global(body[data-theme='dark'] .floating-project-status__submenu) {
background: #2a2a2a !important;
border-color: rgba(255, 255, 255, 0.1);
background: var(--badge-bg) !important;
border-color: var(--border-card-strong);
}
:global(html[data-theme='dark'] .floating-project-status__button:hover),
@ -1990,7 +2047,7 @@ onBeforeUnmount(() => {
:global(body[data-theme='dark'] .floating-project-status__stats:hover),
:global(html[data-theme='dark'] .floating-project-status__submenu button:hover),
:global(body[data-theme='dark'] .floating-project-status__submenu button:hover) {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.28);
box-shadow: 0 1px 2px var(--shadow-color);
}
.floating-status-motion-enter-active,
@ -2027,9 +2084,9 @@ onBeforeUnmount(() => {
border-radius: 999px;
font-size: 12px;
line-height: 1.4;
color: var(--claude-text-secondary, #7f7766);
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));
color: var(--claude-text-secondary);
background: color-mix(in srgb, var(--theme-surface-soft) 92%, var(--claude-text-tertiary) 8%);
border: 1px solid var(--theme-chip-border);
user-select: none;
transition: gap 0.2s ease, padding 0.2s ease;
}
@ -2057,32 +2114,32 @@ onBeforeUnmount(() => {
opacity: 0;
}
.goal-mode-banner--running {
color: var(--claude-accent, #da7756);
border-color: var(--claude-accent, #da7756);
background: color-mix(in srgb, var(--theme-surface-soft, #fffaf0) 82%, var(--claude-accent, #da7756) 18%);
color: var(--claude-accent);
border-color: var(--claude-accent);
background: color-mix(in srgb, var(--theme-surface-soft) 82%, var(--claude-accent) 18%);
}
.goal-mode-banner--done {
color: var(--claude-success, #76b086);
border-color: var(--claude-success, #76b086);
background: color-mix(in srgb, var(--theme-surface-soft, #fffaf0) 82%, var(--claude-success, #76b086) 18%);
color: var(--claude-success);
border-color: var(--claude-success);
background: color-mix(in srgb, var(--theme-surface-soft) 82%, var(--claude-success) 18%);
}
:global(:root[data-theme='dark']) .goal-mode-banner,
:global(body[data-theme='dark']) .goal-mode-banner {
color: var(--claude-text-secondary, #a0a0a0);
background: color-mix(in srgb, var(--theme-surface-muted, #141414) 82%, var(--claude-text-tertiary, #707070) 18%);
border-color: var(--theme-control-border-strong, rgba(255, 255, 255, 0.12));
color: var(--claude-text-secondary);
background: color-mix(in srgb, var(--theme-surface-muted) 82%, var(--claude-text-tertiary) 18%);
border-color: var(--theme-control-border-strong);
}
:global(:root[data-theme='dark']) .goal-mode-banner--running,
:global(body[data-theme='dark']) .goal-mode-banner--running {
color: #e5e7eb;
background: color-mix(in srgb, var(--theme-surface-muted, #141414) 72%, var(--claude-accent, #606060) 28%);
border-color: color-mix(in srgb, var(--claude-accent, #606060) 70%, white 30%);
color: var(--text-primary);
background: color-mix(in srgb, var(--theme-surface-muted) 72%, var(--claude-accent) 28%);
border-color: color-mix(in srgb, var(--claude-accent) 70%, white 30%);
}
:global(:root[data-theme='dark']) .goal-mode-banner--done,
:global(body[data-theme='dark']) .goal-mode-banner--done {
color: #d1fae5;
background: color-mix(in srgb, var(--theme-surface-muted, #141414) 72%, var(--claude-success, #10b981) 28%);
border-color: color-mix(in srgb, var(--claude-success, #10b981) 70%, white 30%);
color: color-mix(in srgb, var(--state-success) 30%, white);
background: color-mix(in srgb, var(--theme-surface-muted) 72%, var(--claude-success) 28%);
border-color: color-mix(in srgb, var(--claude-success) 70%, white 30%);
}
.goal-mode-banner--clickable {
cursor: pointer;
@ -2091,14 +2148,14 @@ onBeforeUnmount(() => {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--claude-text-tertiary, #a59a86);
background: var(--claude-text-tertiary);
}
.goal-mode-banner--running .goal-mode-banner__dot {
background: var(--claude-accent, #da7756);
background: var(--claude-accent);
animation: goal-banner-pulse 1.4s ease-in-out infinite;
}
.goal-mode-banner--done .goal-mode-banner__dot {
background: var(--claude-success, #76b086);
background: var(--claude-success);
}
@keyframes goal-banner-pulse {
0%, 100% { opacity: 1; }
@ -2129,7 +2186,7 @@ onBeforeUnmount(() => {
border-radius: 6px;
overflow: hidden;
cursor: pointer;
border: 1px solid var(--border-color, #2a2f3a);
border: 1px solid var(--border-default);
}
.image-thumbnail {
@ -2144,7 +2201,7 @@ onBeforeUnmount(() => {
top: 0;
right: 0;
background: none;
color: #fff;
color: var(--on-accent);
border: none;
font-size: 20px;
font-weight: bold;
@ -2152,7 +2209,7 @@ onBeforeUnmount(() => {
cursor: pointer;
display: none;
padding: 2px 4px;
text-shadow: 0 0 3px rgba(0, 0, 0, 0.8);
text-shadow: 0 0 3px var(--text-shadow-legible);
transition: color 0.15s ease;
}
@ -2161,6 +2218,6 @@ onBeforeUnmount(() => {
}
.image-remove-btn-hover:hover {
color: #ef4444;
color: var(--state-danger);
}
</style>

View File

@ -240,14 +240,14 @@ const goalCompleted = computed(() => String(props.goalProgress?.status || '').to
.submenu-desc {
display: block;
font-size: 12px;
color: var(--text-secondary, #7f8792);
color: var(--text-secondary);
margin-top: 2px;
}
.menu-entry.goal-entry-active {
color: var(--claude-accent, #da7756);
color: var(--claude-accent);
}
.menu-entry.goal-entry-active .entry-arrow {
color: var(--claude-accent, #da7756);
color: var(--claude-accent);
font-size: 12px;
}
</style>

View File

@ -0,0 +1,273 @@
<template>
<!--
数字滚筒(直达式):每一位是一个固定高度的窗口,内部只放旧值 / 新值两格
一次变化只滚一步,不路过中间数字
- 值变大:旧在上新在下,带子上移 下面的数字顶上来
- 值变小:新在上旧在下,带子先停在下格再回滚 上面的数字压下来
各位用 transition-delay 错开,高位先动,间隔很短
-->
<span class="rolling-number">
<TransitionGroup name="rn-place" tag="span" class="rolling-number__track">
<span
v-for="item in renderPlaces"
:key="item.place"
class="rolling-number__digit"
>
<span
class="rolling-number__strip"
:class="{ 'is-animating': item.state.animate }"
:style="{ '--rn-offset': item.state.offset, transitionDelay: item.state.delay + 'ms' }"
>
<span class="rolling-number__cell">{{ item.state.top }}</span>
<span class="rolling-number__cell">{{ item.state.bottom }}</span>
</span>
</span>
</TransitionGroup>
</span>
</template>
<script setup lang="ts">
import { ref, computed, reactive, nextTick, watch, onBeforeUnmount } from 'vue';
const props = defineProps<{ value: number }>();
// (ms),,
const STEP = 38;
// , css transition
const DUR = 360;
type PlaceState = {
top: number; //
bottom: number; //
offset: number; // :0=,1=
animate: boolean; // (false )
delay: number; // transition-delay,
};
// place:,0=,
const order = ref<number[]>([]); // ()
const states = new Map<number, PlaceState>(); // place => (reactive)
let booted = false;
let gen = 0; // ,
const normalizeTimers = new Set<number>();
const removeTimers = new Map<number, number>();
function digitsOf(v: number): Map<number, number> {
const s = Math.max(0, Math.trunc(Number(v) || 0)).toString();
const len = s.length;
const m = new Map<number, number>();
for (let i = 0; i < len; i++) m.set(len - 1 - i, Number(s[i]));
return m;
}
const renderPlaces = computed(() =>
order.value
.map((p) => ({ place: p, state: states.get(p) as PlaceState }))
.filter((x) => !!x.state)
);
function setOrderDesc(places: Iterable<number>) {
order.value = [...new Set(places)].sort((a, b) => b - a);
}
watch(
() => props.value,
async (val) => {
const target = digitsOf(val);
const myGen = ++gen;
// ,
if (!booted) {
booted = true;
states.clear();
for (const [p, d] of target) {
states.set(p, reactive({ top: d, bottom: d, offset: 0, animate: false, delay: 0 }));
}
setOrderDesc(target.keys());
return;
}
const union = new Set<number>([...states.keys(), ...target.keys()]);
const maxPlace = Math.max(...union, 0);
const ups: PlaceState[] = [];
const downs: PlaceState[] = [];
// ,
for (const p of target.keys()) {
const t = removeTimers.get(p);
if (t) {
window.clearTimeout(t);
removeTimers.delete(p);
}
}
for (const place of union) {
const delay = (maxPlace - place) * STEP;
const nv = target.get(place);
if (nv === undefined) continue; // ,
const existing = states.get(place);
if (!existing) {
// :( TransitionGroup ),
states.set(place, reactive({ top: nv, bottom: nv, offset: 0, animate: false, delay }));
continue;
}
existing.delay = delay;
const ov = existing.top; //
if (ov === nv) {
existing.bottom = nv;
existing.offset = 0;
existing.animate = false;
continue;
}
if (ov < nv) {
// :=,=,offset 0 1()
existing.animate = false;
existing.top = ov;
existing.bottom = nv;
existing.offset = 0;
ups.push(existing);
} else {
// :=,=,offset 1 0()
existing.animate = false;
existing.top = nv;
existing.bottom = ov;
existing.offset = 1;
downs.push(existing);
}
}
// (,)
setOrderDesc(target.keys());
// :
for (const place of [...states.keys()]) {
if (!target.has(place) && !removeTimers.has(place)) {
const t = window.setTimeout(() => {
removeTimers.delete(place);
states.delete(place);
}, 260);
removeTimers.set(place, t);
}
}
// ,
await nextTick();
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (myGen !== gen) return;
for (const s of ups) {
s.animate = true;
s.offset = 1;
}
for (const s of downs) {
s.animate = true;
s.offset = 0;
}
});
});
// : top=offset=0(,)
const settle = maxPlace * STEP + DUR + 80;
const norm = window.setTimeout(() => {
normalizeTimers.delete(norm);
if (myGen !== gen) return;
for (const [p, d] of target) {
const s = states.get(p);
if (s) {
s.animate = false;
s.top = d;
s.bottom = d;
s.offset = 0;
}
}
}, settle);
normalizeTimers.add(norm);
},
{ immediate: true }
);
onBeforeUnmount(() => {
normalizeTimers.forEach((t) => window.clearTimeout(t));
normalizeTimers.clear();
removeTimers.forEach((t) => window.clearTimeout(t));
removeTimers.clear();
});
</script>
<style scoped>
.rolling-number {
display: inline-flex;
vertical-align: baseline;
}
.rolling-number__track {
position: relative;
display: inline-flex;
align-items: stretch;
}
.rolling-number__digit {
--rn-h: 1.2em;
display: inline-block;
height: var(--rn-h);
line-height: var(--rn-h);
overflow: hidden;
}
.rolling-number__strip {
display: flex;
flex-direction: column;
transform: translateY(calc(var(--rn-offset, 0) * var(--rn-h) * -1));
will-change: transform;
}
.rolling-number__strip.is-animating {
transition: transform 360ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.rolling-number__cell {
height: var(--rn-h);
line-height: var(--rn-h);
text-align: center;
font-variant-numeric: inherit;
}
/* 位数增减时整位的进出:新增位淡入下沉,消失位淡出上移 */
.rn-place-enter-active,
.rn-place-leave-active {
transition: opacity 220ms ease, transform 220ms ease;
}
.rn-place-enter-from {
opacity: 0;
transform: translateY(45%);
}
.rn-place-leave-to {
opacity: 0;
transform: translateY(-45%);
}
/* 让消失位脱离布局,其余位平滑左移而非瞬跳 */
.rn-place-leave-active {
position: absolute;
}
.rn-place-move {
transition: transform 220ms ease;
}
@media (prefers-reduced-motion: reduce) {
.rolling-number__strip.is-animating {
transition: none;
}
.rn-place-enter-active,
.rn-place-leave-active,
.rn-place-move {
transition: none;
}
}
</style>

View File

@ -177,7 +177,7 @@ const formatUpdatedAt = (value: string | number) => {
.review-overlay {
position: fixed;
inset: 0;
background: var(--theme-overlay-scrim, rgba(18, 19, 22, 0.4));
background: var(--theme-overlay-scrim);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
@ -248,7 +248,7 @@ const formatUpdatedAt = (value: string | number) => {
color: var(--claude-text-secondary);
padding: 0 10px;
border-radius: 8px;
background: var(--theme-tab-active, rgba(0, 0, 0, 0.05));
background: var(--theme-tab-active);
max-width: 280px;
white-space: nowrap;
text-overflow: ellipsis;
@ -273,7 +273,7 @@ const formatUpdatedAt = (value: string | number) => {
}
.icon-close-btn:hover:not(:disabled) {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.05));
background: var(--theme-tab-active);
color: var(--claude-text);
}
@ -371,7 +371,7 @@ const formatUpdatedAt = (value: string | number) => {
.conversation-item:hover:not(:disabled),
.conversation-item.active {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
/* 选中态:左侧强调条提示,不靠彩色光晕 */
@ -418,7 +418,7 @@ const formatUpdatedAt = (value: string | number) => {
border-radius: 999px;
font-size: 12px;
line-height: 1;
background: var(--theme-chip-bg, rgba(118, 103, 84, 0.12));
background: var(--theme-chip-bg);
color: var(--claude-text-secondary);
}
@ -463,7 +463,7 @@ const formatUpdatedAt = (value: string | number) => {
}
.load-more-btn:hover:not(:disabled) {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
color: var(--claude-text);
}
@ -496,7 +496,7 @@ const formatUpdatedAt = (value: string | number) => {
}
.preview-line:nth-child(odd) {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.04));
background: var(--theme-tab-active);
}
.placeholder {
@ -529,7 +529,7 @@ const formatUpdatedAt = (value: string | number) => {
}
.placeholder.error .text-main {
color: var(--claude-warning, #d99845);
color: var(--claude-warning);
}
/* ===== 底部操作栏 ===== */
@ -568,7 +568,7 @@ const formatUpdatedAt = (value: string | number) => {
width: 38px;
height: 22px;
border-radius: 999px;
background: var(--theme-switch-track, #d7d1c5);
background: var(--theme-switch-track);
position: relative;
transition: background 0.2s ease;
}
@ -579,7 +579,7 @@ const formatUpdatedAt = (value: string | number) => {
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--theme-surface-strong, #ffffff);
background: var(--theme-surface-strong);
top: 2px;
left: 2px;
transition: transform 0.2s ease;
@ -608,7 +608,7 @@ const formatUpdatedAt = (value: string | number) => {
font-weight: 600;
cursor: pointer;
background: var(--claude-accent);
color: #fffaf0;
color: var(--on-accent);
transition:
background 140ms ease,
color 140ms ease;
@ -628,12 +628,6 @@ const formatUpdatedAt = (value: string | number) => {
cursor: default;
}
/* 暗色模式下主按钮文字保持白色accent 在暗色为灰,文字仍需高对比) */
:global(:root[data-theme='dark']) .primary-btn,
:global(body[data-theme='dark']) .primary-btn {
color: #ffffff;
}
/* ===== 窄屏:上下堆叠 ===== */
@media (max-width: 860px) {
.review-body {

View File

@ -167,52 +167,52 @@ const stoppedReasonLabel = computed(() => {
font-size: 12px;
padding: 2px 10px;
border-radius: 999px;
border: 1px solid var(--theme-chip-border, rgba(118, 103, 84, 0.2));
background: color-mix(in srgb, var(--theme-surface-soft, #fffaf0) 92%, var(--claude-text-tertiary, #a59a86) 8%);
color: var(--claude-text-secondary, #7f7766);
border: 1px solid var(--theme-chip-border);
background: color-mix(in srgb, var(--theme-surface-soft) 92%, var(--claude-text-tertiary) 8%);
color: var(--claude-text-secondary);
}
.goal-progress-status.is-running {
color: var(--claude-accent, #da7756);
border-color: var(--claude-accent, #da7756);
background: color-mix(in srgb, var(--theme-surface-soft, #fffaf0) 82%, var(--claude-accent, #da7756) 18%);
color: var(--claude-accent);
border-color: var(--claude-accent);
background: color-mix(in srgb, var(--theme-surface-soft) 82%, var(--claude-accent) 18%);
}
.goal-progress-status.is-done {
color: var(--claude-success, #76b086);
border-color: var(--claude-success, #76b086);
background: color-mix(in srgb, var(--theme-surface-soft, #fffaf0) 82%, var(--claude-success, #76b086) 18%);
color: var(--claude-success);
border-color: var(--claude-success);
background: color-mix(in srgb, var(--theme-surface-soft) 82%, var(--claude-success) 18%);
}
.goal-progress-status.is-stopped {
color: var(--claude-warning, #d99845);
border-color: var(--claude-warning, #d99845);
background: color-mix(in srgb, var(--theme-surface-soft, #fffaf0) 82%, var(--claude-warning, #d99845) 18%);
color: var(--claude-warning);
border-color: var(--claude-warning);
background: color-mix(in srgb, var(--theme-surface-soft) 82%, var(--claude-warning) 18%);
}
:global(:root[data-theme='dark']) .goal-progress-status,
:global(body[data-theme='dark']) .goal-progress-status {
color: var(--claude-text-secondary, #a0a0a0);
background: color-mix(in srgb, var(--theme-surface-muted, #141414) 82%, var(--claude-text-tertiary, #707070) 18%);
border-color: var(--theme-control-border-strong, rgba(255, 255, 255, 0.12));
color: var(--claude-text-secondary);
background: color-mix(in srgb, var(--theme-surface-muted) 82%, var(--claude-text-tertiary) 18%);
border-color: var(--theme-control-border-strong);
}
:global(:root[data-theme='dark']) .goal-progress-status.is-running,
:global(body[data-theme='dark']) .goal-progress-status.is-running {
color: #e5e7eb;
background: color-mix(in srgb, var(--theme-surface-muted, #141414) 72%, var(--claude-accent, #606060) 28%);
border-color: color-mix(in srgb, var(--claude-accent, #606060) 70%, white 30%);
color: var(--text-primary);
background: color-mix(in srgb, var(--theme-surface-muted) 72%, var(--claude-accent) 28%);
border-color: color-mix(in srgb, var(--claude-accent) 70%, white 30%);
}
:global(:root[data-theme='dark']) .goal-progress-status.is-done,
:global(body[data-theme='dark']) .goal-progress-status.is-done {
color: #d1fae5;
background: color-mix(in srgb, var(--theme-surface-muted, #141414) 72%, var(--claude-success, #10b981) 28%);
border-color: color-mix(in srgb, var(--claude-success, #10b981) 70%, white 30%);
color: color-mix(in srgb, var(--state-success) 30%, white);
background: color-mix(in srgb, var(--theme-surface-muted) 72%, var(--claude-success) 28%);
border-color: color-mix(in srgb, var(--claude-success) 70%, white 30%);
}
:global(:root[data-theme='dark']) .goal-progress-status.is-stopped,
:global(body[data-theme='dark']) .goal-progress-status.is-stopped {
color: #ffedd5;
background: color-mix(in srgb, var(--theme-surface-muted, #141414) 72%, var(--claude-warning, #f59e0b) 28%);
border-color: color-mix(in srgb, var(--claude-warning, #f59e0b) 70%, white 30%);
color: color-mix(in srgb, var(--state-warning) 30%, white);
background: color-mix(in srgb, var(--theme-surface-muted) 72%, var(--claude-warning) 28%);
border-color: color-mix(in srgb, var(--claude-warning) 70%, white 30%);
}
.goal-progress-goal {
font-size: 13px;
color: var(--claude-text-secondary, #7f7766);
color: var(--claude-text-secondary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -228,37 +228,37 @@ const stoppedReasonLabel = computed(() => {
text-align: center;
padding: 10px 6px;
border-radius: 10px;
background: var(--theme-surface-muted, rgba(255, 255, 255, 0.85));
border: 1px solid var(--theme-control-border, rgba(118, 103, 84, 0.25));
background: var(--theme-surface-muted);
border: 1px solid var(--theme-control-border);
}
.goal-metric__value {
font-size: 18px;
font-weight: 600;
color: var(--claude-text, #3d3929);
color: var(--claude-text);
}
.goal-metric__label {
margin-top: 4px;
font-size: 11px;
color: var(--claude-text-tertiary, #a59a86);
color: var(--claude-text-tertiary);
}
.goal-progress-reason {
font-size: 13px;
color: var(--claude-warning, #d99845);
color: var(--claude-warning);
margin-bottom: 10px;
}
.goal-progress-summary {
border-top: 1px solid var(--theme-control-border, rgba(118, 103, 84, 0.25));
border-top: 1px solid var(--theme-control-border);
padding-top: 10px;
}
.goal-progress-summary__title {
font-size: 12px;
color: var(--claude-text-tertiary, #a59a86);
color: var(--claude-text-tertiary);
margin-bottom: 6px;
}
.goal-progress-summary__body {
font-size: 13px;
line-height: 1.6;
color: var(--claude-text, #3d3929);
color: var(--claude-text);
white-space: pre-wrap;
word-break: break-word;
max-height: 240px;

View File

@ -184,7 +184,7 @@ const handleOverlayClose = () => {
.host-workspace-dialog__error {
font-size: 13px;
color: #ef4444;
color: var(--state-danger);
}
.host-workspace-dialog__actions {
@ -210,7 +210,7 @@ const handleOverlayClose = () => {
.host-workspace-dialog__btn.primary {
background: var(--claude-accent);
border-color: var(--claude-accent-strong);
color: #fff;
color: var(--on-accent);
}
.host-workspace-dialog__btn:disabled {

View File

@ -300,12 +300,12 @@ const submitRename = (item: WorkspaceItem) => {
justify-self: flex-start;
background: var(--claude-accent);
border-color: var(--claude-accent-strong);
color: #fff;
color: var(--on-accent);
}
.host-workspace-dialog__btn.danger {
color: #ef4444;
border-color: color-mix(in srgb, #ef4444 45%, var(--theme-control-border-strong));
color: var(--state-danger);
border-color: color-mix(in srgb, var(--state-danger) 45%, var(--theme-control-border-strong));
}
.workspace-manage-list {
@ -375,7 +375,7 @@ const submitRename = (item: WorkspaceItem) => {
.host-workspace-dialog__error {
font-size: 13px;
color: #ef4444;
color: var(--state-danger);
}
@media (max-width: 640px) {

View File

@ -134,7 +134,7 @@ onMounted(() => {
.image-picker-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
background: var(--overlay-scrim);
display: flex;
align-items: center;
justify-content: center;
@ -143,20 +143,20 @@ onMounted(() => {
.image-picker-panel {
width: min(980px, 92vw);
max-height: 88vh;
background: #0f1116;
color: #e8ecf2;
border: 1px solid #2a2f3a;
background: var(--surface-panel);
color: var(--text-primary);
border: 1px solid var(--border-default);
border-radius: 12px;
display: flex;
flex-direction: column;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);
box-shadow: 0 16px 40px var(--shadow-color);
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
border-bottom: 1px solid #1f2430;
border-bottom: 1px solid var(--border-default);
}
.header-left {
display: flex;
@ -167,11 +167,11 @@ onMounted(() => {
font-weight: 600;
}
.local-btn {
border: 1px solid #2f3645;
border: 1px solid var(--border-strong);
padding: 4px 10px;
border-radius: 8px;
background: #1b202c;
color: #c5ccda;
background: var(--surface-raised);
color: var(--text-secondary);
font-size: 12px;
cursor: pointer;
}
@ -181,7 +181,7 @@ onMounted(() => {
}
.close-btn {
background: transparent;
color: #9aa3b5;
color: var(--text-tertiary);
border: none;
font-size: 20px;
cursor: pointer;
@ -197,9 +197,9 @@ onMounted(() => {
gap: 12px;
}
.card {
border: 1px solid #1f2430;
border: 1px solid var(--border-default);
border-radius: 10px;
background: #151922;
background: var(--surface-card);
cursor: pointer;
overflow: hidden;
display: flex;
@ -209,49 +209,49 @@ onMounted(() => {
width: 100%;
height: 120px;
object-fit: cover;
background: #0c0f14;
background: var(--surface-base);
}
.card .name {
padding: 8px 10px;
font-size: 12px;
color: #c5ccda;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.card.selected {
border-color: #4ca6ff;
box-shadow: 0 0 0 2px rgba(76, 166, 255, 0.2);
border-color: var(--state-info);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--state-info) 20%, transparent);
}
.loading,
.empty {
padding: 40px 0;
text-align: center;
color: #9aa3b5;
color: var(--text-tertiary);
}
.footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-top: 1px solid #1f2430;
border-top: 1px solid var(--border-default);
}
.actions {
display: flex;
gap: 10px;
}
.btn {
border: 1px solid #2f3645;
border: 1px solid var(--border-strong);
padding: 8px 14px;
border-radius: 8px;
background: #1b202c;
color: #e8ecf2;
background: var(--surface-raised);
color: var(--text-primary);
cursor: pointer;
}
.btn.primary {
background: #4ca6ff;
border-color: #4ca6ff;
color: #0d1117;
background: var(--state-info);
border-color: var(--state-info);
color: var(--text-primary);
}
.btn:disabled {
opacity: 0.5;
@ -259,7 +259,7 @@ onMounted(() => {
}
.count {
font-size: 13px;
color: #9aa3b5;
color: var(--text-tertiary);
}
@media (max-width: 640px) {
.grid {

View File

@ -47,23 +47,23 @@ const emitSkip = () => emit('skip');
display: grid;
place-items: center;
padding: 24px;
background: var(--theme-overlay-scrim, rgba(15, 23, 42, 0.45));
background: var(--theme-overlay-scrim);
backdrop-filter: blur(2px);
}
.new-user-tutorial-dialog {
width: min(520px, calc(100vw - 32px));
border-radius: 18px;
border: 1px solid var(--theme-control-border-strong, rgba(148, 163, 184, 0.35));
background: var(--theme-surface-card, #ffffff);
box-shadow: var(--theme-shadow-strong, 0 24px 64px rgba(15, 23, 42, 0.26));
border: 1px solid var(--theme-control-border-strong);
background: var(--theme-surface-card);
box-shadow: var(--theme-shadow-strong);
padding: 22px 22px 18px;
}
.eyebrow {
margin: 0;
font-size: 12px;
color: var(--theme-text-muted, #64748b);
color: var(--text-tertiary);
}
h3 {
@ -73,7 +73,7 @@ h3 {
.desc {
margin: 0;
color: var(--theme-text-muted, #64748b);
color: var(--text-tertiary);
line-height: 1.6;
}
@ -87,7 +87,7 @@ h3 {
button {
min-height: 38px;
border-radius: 999px;
border: 1px solid var(--theme-control-border, rgba(148, 163, 184, 0.3));
border: 1px solid var(--theme-control-border);
padding: 0 15px;
font-weight: 700;
font-size: 13px;
@ -101,12 +101,12 @@ button {
.primary {
border: none;
background: linear-gradient(135deg, var(--claude-accent), var(--claude-accent-strong));
color: #fff;
color: var(--on-accent);
}
.ghost {
background: var(--theme-surface-soft, rgba(248, 250, 252, 0.9));
color: var(--claude-text, inherit);
background: var(--theme-surface-soft);
color: var(--claude-text);
}
button:hover:not(:disabled) {

View File

@ -50,7 +50,7 @@ defineEmits<{
</script>
<style scoped>
.overlay-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.35); display: flex; align-items: center; justify-content: center; z-index: 1000; }
.overlay-backdrop { position: fixed; inset: 0; background: var(--overlay-scrim); display: flex; align-items: center; justify-content: center; z-index: 1000; }
.overlay-card { width: min(680px, 92vw); background: var(--theme-surface-soft); border: 1px solid var(--theme-control-border); border-radius: 12px; padding: 12px; }
.overlay-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.overlay-header h3 { margin: 0; font-size: 16px; }

View File

@ -192,12 +192,12 @@ function submit() {
width: 12px;
height: 12px;
border-radius: 50%;
border: 1px solid rgba(0, 0, 0, 0.12);
border: 1px solid var(--border-strong);
padding: 0;
cursor: pointer;
}
.traffic-dot--close { background: #ff5f57; }
.traffic-dot--close { background: var(--mac-close); }
.user-question-window-title {
text-align: center;
@ -266,7 +266,7 @@ function submit() {
}
.user-question-nav button:hover:not(:disabled) {
background: var(--theme-hover-bg, rgba(0, 0, 0, 0.055));
background: var(--hover-bg);
color: var(--claude-text);
}
@ -318,7 +318,7 @@ function submit() {
}
.user-question-option:hover {
background: var(--theme-hover-bg, rgba(0, 0, 0, 0.055));
background: var(--hover-bg);
}
.user-question-option.active {
@ -408,18 +408,18 @@ function submit() {
}
.user-question-btn.ghost:hover {
background: var(--theme-hover-bg, rgba(0, 0, 0, 0.055));
background: var(--hover-bg);
color: var(--claude-text);
}
.user-question-btn.primary {
background: var(--claude-accent);
border-color: var(--claude-accent-strong);
color: #fffaf0;
color: var(--on-accent);
}
.user-question-btn.primary:hover:not(:disabled) {
background: var(--claude-button-hover, var(--claude-accent-strong));
background: var(--claude-button-hover);
}
.user-question-btn:disabled {

View File

@ -318,7 +318,7 @@ const formatTime = (value: string) => {
width: 38px;
height: 22px;
border-radius: 999px;
background: var(--theme-switch-track, #d7d1c5);
background: var(--theme-switch-track);
position: relative;
transition: background 0.2s ease;
}
@ -329,7 +329,7 @@ const formatTime = (value: string) => {
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--theme-surface-strong, #ffffff);
background: var(--theme-surface-strong);
top: 2px;
left: 2px;
transition: transform 0.2s ease;
@ -386,7 +386,7 @@ const formatTime = (value: string) => {
}
.scope-select-button:hover:not(:disabled) {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.05));
background: var(--theme-tab-active);
}
.scope-select.disabled .scope-select-button,
@ -435,7 +435,7 @@ const formatTime = (value: string) => {
padding: 6px;
border: 1px solid var(--claude-border);
border-radius: 12px;
background: var(--theme-surface-strong, #fff);
background: var(--theme-surface-strong);
box-shadow: var(--theme-shadow-soft);
display: flex;
flex-direction: column;
@ -461,7 +461,7 @@ const formatTime = (value: string) => {
}
.scope-menu-option:hover:not(:disabled) {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.05));
background: var(--theme-tab-active);
}
.scope-menu-option.disabled,
@ -492,7 +492,7 @@ const formatTime = (value: string) => {
}
.refresh-btn:hover:not(:disabled) {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.05));
background: var(--theme-tab-active);
}
.refresh-btn:disabled {
@ -516,7 +516,7 @@ const formatTime = (value: string) => {
}
.icon-close-btn:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.05));
background: var(--theme-tab-active);
color: var(--claude-text);
}
@ -572,7 +572,7 @@ const formatTime = (value: string) => {
.checkpoint-item:hover,
.checkpoint-item.active {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
.checkpoint-item.active {
@ -622,11 +622,11 @@ const formatTime = (value: string) => {
/* diff 红绿增删色:通用语义色,保留固定值,三模式下含义一致 */
.plus {
color: #16a34a;
color: var(--state-success);
}
.minus {
color: #dc2626;
color: var(--state-danger);
}
.files {
@ -671,11 +671,11 @@ const formatTime = (value: string) => {
}
.file-row:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.04));
background: var(--theme-tab-active);
}
.file-row.expanded {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
.status {
@ -719,7 +719,7 @@ const formatTime = (value: string) => {
.workspace-mismatch-tip {
margin-right: auto;
color: var(--claude-warning, #d99845);
color: var(--claude-warning);
font-size: 13px;
font-weight: 600;
}
@ -732,7 +732,7 @@ const formatTime = (value: string) => {
font-size: 14px;
font-weight: 600;
background: var(--claude-accent);
color: #fffaf0;
color: var(--on-accent);
cursor: pointer;
transition: background 140ms ease;
}
@ -746,12 +746,6 @@ const formatTime = (value: string) => {
cursor: not-allowed;
}
/* 暗色下主按钮文字保持高对比 */
:global(:root[data-theme='dark']) .confirm-btn,
:global(body[data-theme='dark']) .confirm-btn {
color: #ffffff;
}
/* ===== diff 展示:与文件编辑差异保持一致 ===== */
.tool-result-diff {
margin-top: 10px;
@ -765,7 +759,7 @@ const formatTime = (value: string) => {
border: 1px solid var(--claude-border);
border-radius: 8px;
padding: 8px;
background: var(--theme-surface-soft, transparent);
background: var(--theme-surface-soft);
}
.diff-line {
@ -777,13 +771,13 @@ const formatTime = (value: string) => {
/* diff 增删色:通用语义色,保留固定值 */
.diff-remove {
background: rgba(220, 38, 38, 0.12);
color: #dc2626;
background: var(--diff-del-bg);
color: var(--state-danger);
}
.diff-add {
background: rgba(22, 163, 74, 0.12);
color: #16a34a;
background: var(--diff-add-bg);
color: var(--state-success);
}
.diff-context {

View File

@ -139,7 +139,7 @@ onMounted(() => {
.image-picker-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
background: var(--overlay-scrim);
display: flex;
align-items: center;
justify-content: center;
@ -148,20 +148,20 @@ onMounted(() => {
.image-picker-panel {
width: min(780px, 92vw);
max-height: 88vh;
background: #0f1116;
color: #e8ecf2;
border: 1px solid #2a2f3a;
background: var(--surface-panel);
color: var(--text-primary);
border: 1px solid var(--border-default);
border-radius: 12px;
display: flex;
flex-direction: column;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);
box-shadow: 0 16px 40px var(--shadow-color);
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
border-bottom: 1px solid #1f2430;
border-bottom: 1px solid var(--border-default);
}
.header-left {
display: flex;
@ -172,11 +172,11 @@ onMounted(() => {
font-weight: 600;
}
.local-btn {
border: 1px solid #2f3645;
border: 1px solid var(--border-strong);
padding: 4px 10px;
border-radius: 8px;
background: #1b202c;
color: #c5ccda;
background: var(--surface-raised);
color: var(--text-secondary);
font-size: 12px;
cursor: pointer;
}
@ -186,7 +186,7 @@ onMounted(() => {
}
.close-btn {
background: transparent;
color: #9aa3b5;
color: var(--text-tertiary);
border: none;
font-size: 20px;
cursor: pointer;
@ -202,9 +202,9 @@ onMounted(() => {
gap: 12px;
}
.card {
border: 1px solid #1f2430;
border: 1px solid var(--border-default);
border-radius: 10px;
background: #151922;
background: var(--surface-card);
cursor: pointer;
overflow: hidden;
display: flex;
@ -214,61 +214,61 @@ onMounted(() => {
.video-thumb {
height: 110px;
border-radius: 8px;
background: linear-gradient(135deg, rgba(76, 166, 255, 0.12), rgba(76, 166, 255, 0.05));
border: 1px solid #243144;
background: linear-gradient(135deg, color-mix(in srgb, var(--state-info) 12%, transparent), color-mix(in srgb, var(--state-info) 5%, transparent));
border: 1px solid var(--border-strong);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
color: #d9e6ff;
color: var(--text-primary);
gap: 6px;
font-size: 28px;
}
.video-thumb .ext {
font-size: 12px;
color: #9fb7d8;
color: var(--text-secondary);
}
.card .name {
padding: 8px 2px 0;
font-size: 12px;
color: #c5ccda;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.card.selected {
border-color: #4ca6ff;
box-shadow: 0 0 0 2px rgba(76, 166, 255, 0.2);
border-color: var(--state-info);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--state-info) 20%, transparent);
}
.loading,
.empty {
padding: 40px 0;
text-align: center;
color: #9aa3b5;
color: var(--text-tertiary);
}
.footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-top: 1px solid #1f2430;
border-top: 1px solid var(--border-default);
}
.actions {
display: flex;
gap: 10px;
}
.btn {
border: 1px solid #2f3645;
border: 1px solid var(--border-strong);
padding: 8px 14px;
border-radius: 8px;
background: #1b202c;
color: #e8ecf2;
background: var(--surface-raised);
color: var(--text-primary);
cursor: pointer;
}
.btn.primary {
background: #4ca6ff;
border-color: #4ca6ff;
color: #0d1117;
background: var(--state-info);
border-color: var(--state-info);
color: var(--text-primary);
}
.btn:disabled {
opacity: 0.5;
@ -276,7 +276,7 @@ onMounted(() => {
}
.count {
font-size: 13px;
color: #9aa3b5;
color: var(--text-tertiary);
}
@media (max-width: 640px) {
.grid {

View File

@ -250,9 +250,9 @@ const parseFinalMessage = (text: string) => {
.auto-approval-block {
margin-top: 12px;
padding: 10px;
border: 1px solid var(--theme-control-border, var(--border-color, #2a2f3a));
border: 1px solid var(--theme-control-border);
border-radius: 8px;
background: var(--theme-surface-muted, var(--panel-bg, rgba(0, 0, 0, 0.12)));
background: var(--theme-surface-muted);
}
.auto-approval-block__title {
@ -281,11 +281,11 @@ const parseFinalMessage = (text: string) => {
}
.auto-approval-block__decision--approved {
color: #22c55e;
color: var(--state-success);
}
.auto-approval-block__decision--rejected {
color: #ef4444;
color: var(--state-danger);
}
.auto-approval-block__reason {
@ -294,8 +294,8 @@ const parseFinalMessage = (text: string) => {
}
.tool-approval-panel.is-goal-approval-mode .auto-approval-block {
background: var(--theme-surface-card, var(--theme-surface-soft));
border-color: var(--theme-control-border, var(--claude-border));
background: var(--theme-surface-card);
border-color: var(--theme-control-border);
color: var(--claude-text);
}
@ -310,12 +310,12 @@ const parseFinalMessage = (text: string) => {
:global(:root[data-theme='dark']) .auto-approval-block,
:global(body[data-theme='dark']) .auto-approval-block {
background: color-mix(in srgb, var(--theme-surface-muted, #141414) 86%, white 4%);
border-color: var(--theme-control-border-strong, rgba(255, 255, 255, 0.12));
color: var(--claude-text, #ffffff);
background: color-mix(in srgb, var(--theme-surface-muted) 86%, white 4%);
border-color: var(--theme-control-border-strong);
color: var(--claude-text);
}
:global(:root[data-theme='dark']) .auto-approval-block__content,
:global(body[data-theme='dark']) .auto-approval-block__content {
color: var(--claude-text-secondary, #a0a0a0);
color: var(--claude-text-secondary);
}
</style>

View File

@ -93,7 +93,7 @@ function confirmSelection() {
.exp-overlay {
position: fixed;
inset: 0;
background: rgba(33, 24, 14, 0.55);
background: var(--overlay-scrim);
display: flex;
align-items: center;
justify-content: center;
@ -102,11 +102,11 @@ function confirmSelection() {
}
.exp-card {
width: min(1100px, 96vw);
background: rgba(255, 255, 255, 0.96);
background: var(--surface-soft);
border-radius: 18px;
padding: 24px 28px;
box-shadow: none;
border: 1px solid rgba(118, 103, 84, 0.25);
border: 1px solid var(--border-default);
}
.exp-header {
display: flex;
@ -117,17 +117,17 @@ function confirmSelection() {
}
.title {
font-size: 22px;
color: #3d3929;
color: var(--text-primary);
font-weight: 700;
}
.subtitle {
font-size: 14px;
color: #7f7766;
color: var(--text-secondary);
}
.confirm-btn {
border: none;
background: #da7756;
color: #fff;
background: var(--accent);
color: var(--on-accent);
border-radius: 12px;
padding: 10px 18px;
font-weight: 600;
@ -139,20 +139,20 @@ function confirmSelection() {
gap: 16px;
}
.mode-card {
border: 1px solid rgba(118, 103, 84, 0.25);
border: 1px solid var(--border-default);
border-radius: 14px;
padding: 16px 14px;
cursor: pointer;
transition: 0.2s ease;
box-shadow: none;
background: #fff;
background: var(--surface-raised);
}
.mode-card:hover {
transform: translateY(-2px);
box-shadow: none;
}
.mode-card.active {
border-color: #da7756;
border-color: var(--accent);
box-shadow: none;
}
.badge {
@ -160,8 +160,8 @@ function confirmSelection() {
align-items: center;
gap: 6px;
font-size: 12px;
color: #bd5d3a;
background: rgba(218, 119, 86, 0.14);
color: var(--accent-strong);
background: var(--highlight);
padding: 4px 10px;
border-radius: 20px;
margin-bottom: 8px;
@ -170,12 +170,12 @@ function confirmSelection() {
width: 52px;
height: 52px;
border-radius: 14px;
background: rgba(218, 119, 86, 0.12);
background: var(--highlight);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 10px;
color: #bd5d3a;
color: var(--accent-strong);
}
.icon svg {
width: 30px;
@ -184,12 +184,12 @@ function confirmSelection() {
.mode-card h3 {
margin: 0 0 6px;
font-size: 17px;
color: #3d3929;
color: var(--text-primary);
}
.mode-card p {
margin: 0;
font-size: 13px;
line-height: 1.5;
color: #7f7766;
color: var(--text-secondary);
}
</style>

View File

@ -1779,7 +1779,9 @@ const blockDisplayLabel = computed(() => {
);
});
const currentCompactMessageDisplay = computed(() => experiments.value.compactMessageDisplay);
const currentCompactMessageDisplay = computed(
() => form.value.compact_message_display || 'full'
);
const compactMessageDisplayLabel = computed(() => {
return (
@ -2351,29 +2353,29 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
<style scoped>
.settings-redesign-card {
--settings-floating-menu-bg: #ffffff;
--settings-floating-menu-hover: rgba(0, 0, 0, 0.055);
--settings-floating-menu-bg: color-mix(in srgb, var(--surface-soft) 90%, var(--surface-raised));
--settings-floating-menu-hover: var(--hover-bg);
--settings-floating-menu-shadow: none;
--settings-tab-hover-bg: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
--settings-tab-active-bg: var(--theme-tab-active, rgba(0, 0, 0, 0.078));
--settings-tab-hover-bg: var(--theme-tab-active);
--settings-tab-active-bg: var(--theme-tab-active);
position: relative;
width: min(70vw, calc(100vw - 24px));
height: min(80vh, calc(100vh - 24px));
padding: 30px 42px 24px;
border-radius: 32px;
overflow: visible;
background: var(--theme-surface-soft, var(--claude-panel));
border: 1px solid var(--theme-control-border, var(--claude-border));
background: var(--theme-surface-soft);
border: 1px solid var(--theme-control-border);
box-shadow: none;
}
:global(html[data-theme='dark']) .settings-redesign-card,
:global(body[data-theme='dark']) .settings-redesign-card {
--settings-floating-menu-bg: #1a1a1a;
--settings-floating-menu-hover: rgba(255, 255, 255, 0.08);
--settings-floating-menu-bg: var(--surface-panel);
--settings-floating-menu-hover: var(--hover-bg);
--settings-floating-menu-shadow: none;
--settings-tab-hover-bg: rgba(255, 255, 255, 0.078);
--settings-tab-active-bg: rgba(255, 255, 255, 0.078);
--settings-tab-hover-bg: var(--hover-bg);
--settings-tab-active-bg: var(--hover-bg);
}
.settings-close-button {
@ -2393,7 +2395,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
}
.settings-close-button:hover {
background: var(--theme-hover-bg, rgba(0, 0, 0, 0.06));
background: var(--hover-bg);
}
.settings-close-button svg {
@ -2420,7 +2422,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
margin-top: 68px;
height: calc(100% - 68px);
padding-right: 28px;
border-right: 1px solid var(--theme-control-border, var(--claude-border));
border-right: 1px solid var(--theme-control-border);
display: flex;
flex-direction: column;
gap: 3px;
@ -2507,7 +2509,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
.settings-redesign-title-line {
height: 1px;
background: var(--theme-control-border, var(--claude-border));
background: var(--theme-control-border);
margin: 22px 0 8px;
flex: 0 0 1px;
}
@ -2549,7 +2551,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
.settings-action-row,
.settings-toggle-row {
min-height: 64px;
border-bottom: 1px solid var(--theme-control-border, var(--claude-border));
border-bottom: 1px solid var(--theme-control-border);
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
@ -2614,7 +2616,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
}
.settings-select-button:hover {
background: var(--theme-hover-bg, rgba(0, 0, 0, 0.055));
background: var(--hover-bg);
}
.settings-select-button .select-chevron {
@ -2649,7 +2651,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
padding: 6px;
border-radius: 20px;
background: var(--settings-floating-menu-bg);
border: 1px solid var(--theme-control-border, var(--claude-border));
border: 1px solid var(--theme-control-border);
box-shadow: var(--settings-floating-menu-shadow);
opacity: 1;
backdrop-filter: none;
@ -2662,23 +2664,23 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
}
.settings-floating-menu.dark {
background: #1a1a1a !important;
background: var(--surface-panel) !important;
box-shadow: none !important;
}
.settings-floating-menu.dark .settings-menu-option:hover {
background: rgba(255, 255, 255, 0.08) !important;
background: var(--hover-bg) !important;
}
:global(html[data-theme='dark']) :global(.settings-floating-menu),
:global(body[data-theme='dark']) :global(.settings-floating-menu) {
background: #1a1a1a !important;
background: var(--surface-panel) !important;
box-shadow: none !important;
}
:global(html[data-theme='dark']) :global(.settings-menu-option:hover),
:global(body[data-theme='dark']) :global(.settings-menu-option:hover) {
background: rgba(255, 255, 255, 0.08) !important;
background: var(--hover-bg) !important;
}
.settings-menu-option {
@ -2757,7 +2759,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
.settings-number-input,
.settings-compression-grid input {
height: 38px;
border: 1px solid var(--theme-control-border, var(--claude-border));
border: 1px solid var(--theme-control-border);
border-radius: 12px;
background: transparent;
color: var(--claude-text);
@ -2779,7 +2781,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
.settings-section-divider__label {
font-size: 12px;
font-weight: 600;
color: var(--claude-text-secondary, #7f7766);
color: var(--claude-text-secondary);
letter-spacing: 0.04em;
}
.settings-section-divider::after {
@ -2787,7 +2789,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
flex: 1;
height: 1px;
margin-left: 10px;
background: var(--theme-control-border, var(--claude-border));
background: var(--theme-control-border);
}
.settings-input-row > input {
@ -2834,7 +2836,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
.settings-add-row button,
.settings-secondary-button,
.settings-primary-button {
border: 1px solid var(--theme-control-border, var(--claude-border));
border: 1px solid var(--theme-control-border);
border-radius: 999px;
background: transparent;
color: var(--claude-text);
@ -2848,18 +2850,18 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
.settings-number-row button:hover,
.settings-add-row button:hover,
.settings-secondary-button:hover {
background: var(--theme-hover-bg, rgba(0, 0, 0, 0.055));
background: var(--hover-bg);
}
.settings-chip-row button.active,
.settings-primary-button {
border-color: var(--claude-accent);
background: var(--claude-accent);
color: #fff;
color: var(--on-accent);
}
.settings-secondary-button.danger {
color: var(--claude-warning, #c85f45);
color: var(--claude-warning);
}
.settings-inline-actions {
@ -2880,7 +2882,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
}
.settings-mini-status.warning {
color: var(--claude-warning, #c85f45);
color: var(--claude-warning);
}
.settings-add-row,
@ -2907,7 +2909,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
.settings-consideration-list li {
min-height: 34px;
border: 1px solid var(--theme-control-border, var(--claude-border));
border: 1px solid var(--theme-control-border);
border-radius: 12px;
padding: 0 8px;
display: grid;
@ -2980,7 +2982,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
}
.settings-group-block {
border-bottom: 1px solid var(--theme-control-border, var(--claude-border));
border-bottom: 1px solid var(--theme-control-border);
padding: 14px 0 16px;
}
@ -3099,7 +3101,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
.usage-summary-item {
padding: 12px 14px;
border-radius: 14px;
border: 1px solid var(--theme-control-border, var(--claude-border));
border: 1px solid var(--theme-control-border);
background: transparent;
}
@ -3132,7 +3134,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
}
.usage-summary-refresh {
border: 1px solid var(--theme-control-border, var(--claude-border));
border: 1px solid var(--theme-control-border);
border-radius: 999px;
padding: 7px 14px;
background: transparent;
@ -3170,7 +3172,7 @@ const applyDefaultHideWorkspaceOption = async (hidden: boolean) => {
padding-right: 0;
padding-bottom: 12px;
border-right: 0;
border-bottom: 1px solid var(--theme-control-border, var(--claude-border));
border-bottom: 1px solid var(--theme-control-border);
flex-direction: row;
overflow: auto;
scrollbar-width: none;

View File

@ -21,6 +21,7 @@ interface PersonalForm {
skill_strict_run_command_background_enabled: boolean;
silent_tool_disable: boolean;
enhanced_tool_display: boolean;
compact_message_display: CompactMessageDisplay;
show_git_status_bar: boolean;
enhanced_tool_display_categories: string[];
enabled_skills: string[];
@ -116,6 +117,7 @@ const defaultForm = (): PersonalForm => ({
skill_strict_run_command_background_enabled: false,
silent_tool_disable: false,
enhanced_tool_display: true,
compact_message_display: 'full',
show_git_status_bar: true,
enhanced_tool_display_categories: [],
enabled_skills: [],
@ -295,6 +297,7 @@ export const usePersonalizationStore = defineStore('personalization', {
!!data.skill_strict_run_command_background_enabled,
silent_tool_disable: !!data.silent_tool_disable,
enhanced_tool_display: data.enhanced_tool_display !== false,
compact_message_display: data.compact_message_display === 'brief' ? 'brief' : 'full',
show_git_status_bar: data.show_git_status_bar !== false,
enhanced_tool_display_categories: Array.isArray(data.enhanced_tool_display_categories)
? data.enhanced_tool_display_categories.filter((item: unknown) => typeof item === 'string')
@ -381,6 +384,18 @@ export const usePersonalizationStore = defineStore('personalization', {
if (this.form.default_hide_workspace) {
useUiStore().setWorkspaceCollapsed(true);
}
// 简略消息显示:以配置文件为准,同步到旧版 localStorage 镜像供 ChatArea 读取。
// 一次性迁移:后端仍为默认 full但本地缓存遗留 brief旧版纯前端记录回写到配置文件。
const cachedCompact = this.experiments.compactMessageDisplay;
if (this.form.compact_message_display === 'full' && cachedCompact === 'brief') {
this.form = { ...this.form, compact_message_display: 'brief' };
void this.persistCompactMessageDisplay('brief');
}
this.experiments = {
...this.experiments,
compactMessageDisplay: this.form.compact_message_display
};
this.persistExperiments();
this.clearFeedback();
},
applyTheme(theme: 'classic' | 'light' | 'dark') {
@ -791,11 +806,35 @@ export const usePersonalizationStore = defineStore('personalization', {
this.persistExperiments();
},
setCompactMessageDisplay(mode: CompactMessageDisplay) {
const target: CompactMessageDisplay = mode === 'brief' ? 'brief' : 'full';
this.form = {
...this.form,
compact_message_display: target
};
// 同步旧版 localStorage 镜像,保持兼容并避免迁移逻辑回写旧值
this.experiments = {
...this.experiments,
compactMessageDisplay: mode
compactMessageDisplay: target
};
this.persistExperiments();
this.clearFeedback();
// 持久化到后端配置文件(即时保存,参照主题/默认隐藏工作区)
void this.persistCompactMessageDisplay(target);
},
async persistCompactMessageDisplay(mode: CompactMessageDisplay) {
try {
const resp = await fetch('/api/personalization', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ compact_message_display: mode })
});
const result = await resp.json();
if (!resp.ok || !result.success) {
console.warn('保存简略消息显示失败:', result?.error);
}
} catch (error) {
console.warn('保存简略消息显示失败:', error);
}
},
setCommunicationStyle(style: 'default' | 'human_like') {
this.form = {

View File

@ -2,7 +2,7 @@
html,
body {
height: var(--app-viewport, 100vh);
height: var(--app-viewport);
}
body {

View File

@ -1,186 +1,318 @@
/* CSS variables + shared design tokens */
/* 设计 Token —— 两层结构:语义层(按主题取值)+ 兼容别名层 */
/* 规范见 doc/frontend/color_token_spec.md。本文件为结构重构产物值与重构前逐一致零视觉变化。 */
:root {
color-scheme: light;
/* 主题无关的布局变量 */
--app-viewport: 100vh;
--app-bottom-inset: env(safe-area-inset-bottom, 0px);
--claude-bg: #eeece2;
--claude-panel: rgba(255, 255, 255, 0.82);
--claude-left-rail: #f7f3ea;
--claude-sidebar: rgba(255, 255, 255, 0.68);
--claude-border: rgba(118, 103, 84, 0.25);
--claude-border-strong: rgba(118, 103, 84, 0.35);
--claude-text: #3d3929;
--claude-text-secondary: #7f7766;
--claude-text-tertiary: #a59a86;
--claude-muted: rgba(121, 109, 94, 0.4);
--claude-accent: #da7756;
--claude-accent-strong: #bd5d3a;
--claude-deep: #f2a93b;
--claude-deep-strong: #d07a14;
--claude-highlight: rgba(218, 119, 86, 0.14);
--claude-button-hover: #c76541;
--claude-button-active: #a95331;
--claude-shadow: 0 14px 36px rgba(61, 57, 41, 0.12);
--claude-success: #16a34a;
--claude-warning: #d99845;
--claude-danger: #dc2626;
--git-diff-add-text: #00e000;
--git-diff-del-text: #ff1a1a;
/* Theme-neutral surfaces */
--theme-surface-card: #fffaf4;
--theme-surface-strong: #ffffff;
--theme-surface-soft: rgba(255, 255, 255, 0.92);
--theme-surface-muted: rgba(255, 255, 255, 0.85);
--theme-overlay-scrim: rgba(33, 24, 14, 0.55);
--theme-shadow-strong: 0 28px 60px rgba(38, 28, 18, 0.25);
--theme-shadow-soft: 0 12px 24px rgba(38, 28, 18, 0.12);
--theme-shadow-mid: 0 20px 45px rgba(16, 24, 40, 0.08);
--theme-control-border: rgba(118, 103, 84, 0.25);
--theme-control-border-strong: rgba(118, 103, 84, 0.35);
--theme-switch-track: #d7d1c5;
--theme-chip-bg: rgba(118, 103, 84, 0.08);
--theme-chip-border: rgba(118, 103, 84, 0.2);
--theme-badge-bg: rgba(118, 103, 84, 0.12);
--theme-tab-active: rgba(189, 93, 58, 0.12);
--theme-mobile-menu: rgba(255, 255, 255, 0.35);
--theme-mobile-menu-shadow: 0 12px 30px rgba(38, 28, 18, 0.15);
--theme-card-border-strong: rgba(118, 103, 84, 0.25);
/* 装饰色主题无关固定品牌色macOS 风格窗口关闭红绿灯 */
--mac-close: #ff5f57;
--mac-close-hover: #ff3b30;
--mac-close-border: #e0443e;
/* 媒体上的文字可读性阴影(主题无关,需固定强暗影压住任意底图) */
--text-shadow-legible: rgba(0, 0, 0, 0.8);
/* 彩蛋装饰色主题无关固定festive色 */
--decorative-gold: #ffd700;
--decorative-pink: #ff69b4;
/* 玻璃质感/实验视觉装饰光斑(主题无关,固定品牌渐变色,刻意不随主题变) */
--glass-tint-blue: rgba(104, 147, 255, 0.2);
--glass-tint-blue-soft: rgba(128, 183, 255, 0.18);
--glass-tint-blue-mid: rgba(140, 180, 255, 0.35);
--glass-tint-amber: rgba(255, 200, 150, 0.22);
--glass-base-deep: rgba(28, 27, 41, 0.95);
--glass-shadow-deep: rgba(24, 20, 37, 0.45);
/* 进度环未完成轨道(主题无关,固定中性灰) */
--progress-track: rgba(127, 135, 146, 0.28);
/* 拖拽预览浮层投影(主题无关,固定黑投影) */
--drag-preview-shadow: rgba(0, 0, 0, 0.35);
/* === 兼容别名(旧 token 名 → 新语义 token迁移完成后删除 === */
--claude-bg: var(--surface-base);
--claude-panel: var(--surface-panel);
--claude-left-rail: var(--surface-rail);
--claude-sidebar: var(--surface-sidebar);
--theme-surface-card: var(--surface-card);
--theme-surface-strong: var(--surface-raised);
--theme-surface-soft: var(--surface-soft);
--theme-surface-muted: var(--surface-muted);
--claude-text: var(--text-primary);
--claude-text-secondary: var(--text-secondary);
--claude-text-tertiary: var(--text-tertiary);
--claude-muted: var(--text-muted);
--claude-border: var(--border-default);
--claude-border-strong: var(--border-strong);
--theme-card-border-strong: var(--border-card-strong);
--theme-chip-border: var(--chip-border);
--claude-accent: var(--accent);
--claude-accent-strong: var(--accent-strong);
--claude-button-hover: var(--accent-hover);
--claude-button-active: var(--accent-active);
--claude-deep: var(--accent-deep);
--claude-deep-strong: var(--accent-deep-strong);
--claude-success: var(--state-success);
--claude-warning: var(--state-warning);
--claude-danger: var(--state-danger);
--claude-highlight: var(--highlight);
--theme-tab-active: var(--tab-active);
--theme-chip-bg: var(--chip-bg);
--theme-badge-bg: var(--badge-bg);
--theme-switch-track: var(--switch-track);
--claude-shadow: var(--shadow-card);
--theme-shadow-soft: var(--shadow-soft);
--theme-shadow-mid: var(--shadow-mid);
--theme-shadow-strong: var(--shadow-strong);
--theme-overlay-scrim: var(--overlay-scrim);
--theme-mobile-menu: var(--mobile-menu);
--theme-mobile-menu-shadow: var(--mobile-menu-shadow);
--git-diff-add-text: var(--diff-add-text);
--git-diff-del-text: var(--diff-del-text);
/* 合并项:下列旧名与上面某新名同值,统一指向合并后的语义 token */
--theme-control-border: var(--border-default);
--theme-control-border-strong: var(--border-strong);
}
:root[data-theme='classic'] {
color-scheme: light;
--claude-bg: #eeece2;
--claude-panel: rgba(255, 255, 255, 0.82);
--claude-left-rail: #f7f3ea;
--claude-sidebar: rgba(255, 255, 255, 0.68);
--claude-border: rgba(118, 103, 84, 0.25);
--claude-border-strong: rgba(118, 103, 84, 0.35);
--claude-text: #3d3929;
--claude-text-secondary: #7f7766;
--claude-text-tertiary: #a59a86;
--claude-muted: rgba(121, 109, 94, 0.4);
--claude-accent: #da7756;
--claude-accent-strong: #bd5d3a;
--claude-deep: #f2a93b;
--claude-deep-strong: #d07a14;
--claude-highlight: rgba(218, 119, 86, 0.14);
--claude-button-hover: #c76541;
--claude-button-active: #a95331;
--claude-shadow: 0 14px 36px rgba(61, 57, 41, 0.12);
--claude-success: #16a34a;
--claude-warning: #d99845;
--claude-danger: #dc2626;
--git-diff-add-text: #00e000;
--git-diff-del-text: #ff1a1a;
--theme-surface-card: #fffaf4;
--theme-surface-strong: #ffffff;
--theme-surface-soft: rgba(255, 255, 255, 0.92);
--theme-surface-muted: rgba(255, 255, 255, 0.85);
--theme-overlay-scrim: rgba(33, 24, 14, 0.55);
--theme-shadow-strong: 0 28px 60px rgba(38, 28, 18, 0.25);
--theme-shadow-soft: 0 12px 24px rgba(38, 28, 18, 0.12);
--theme-shadow-mid: 0 20px 45px rgba(16, 24, 40, 0.08);
--theme-control-border: rgba(118, 103, 84, 0.25);
--theme-control-border-strong: rgba(118, 103, 84, 0.35);
--theme-switch-track: #d7d1c5;
--theme-chip-bg: rgba(118, 103, 84, 0.08);
--theme-chip-border: rgba(118, 103, 84, 0.2);
--theme-badge-bg: rgba(118, 103, 84, 0.12);
--theme-tab-active: rgba(189, 93, 58, 0.12);
--theme-mobile-menu: rgba(255, 255, 255, 0.35);
--theme-mobile-menu-shadow: 0 12px 30px rgba(38, 28, 18, 0.15);
--theme-card-border-strong: rgba(118, 103, 84, 0.25);
/* Surface 表面层级 —— Claude 亮色暖奶油盘canvas<soft<card<cream-strongraised纯白浮起 */
--surface-base: #faf9f5;
--surface-panel: #faf9f5;
--surface-rail: #f5f0e8;
--surface-sidebar: #f5f0e8;
--surface-card: #efe9de;
--surface-raised: #ffffff;
--surface-soft: #f5f0e8;
--surface-muted: #e8e0d2;
/* Text 文字 —— Claude */
--text-primary: #141413;
--text-secondary: #3d3d3a;
--text-tertiary: #6c6a64;
--text-muted: rgba(108, 106, 100, 0.55);
/* Border 边框 —— Claude hairline */
--border-default: #e6dfd8;
--border-strong: #d8cfc4;
--border-card-strong: #e6dfd8;
--chip-border: #ebe6df;
/* Accent 强调 —— Claude primary克制 CTA-only */
--accent: #cc785c;
--accent-strong: #a9583e;
--accent-hover: #bd6a4f;
--accent-active: #a9583e;
--accent-deep: #6c6a64;
--accent-deep-strong: #3d3d3a;
--on-accent: #ffffff;
/* State 状态 —— Claude semantic */
--state-success: #5db872;
--state-warning: #d4a017;
--state-danger: #c64545;
--state-danger-strong: #a83838;
--status-offline: #c64545;
--state-info: #5db8a6;
/* Highlight / 交互态 —— 中性暖灰(不带橙) */
--highlight: rgba(20, 20, 19, 0.05);
--tab-active: rgba(20, 20, 19, 0.07);
--chip-bg: #f5f0e8;
--badge-bg: #efe9de;
--switch-track: #d8cfc4;
--hover-bg: rgba(20, 20, 19, 0.05);
--pulse-ring: rgba(0, 0, 0, 0.1);
/* Shadow 阴影 */
--shadow-card: 0 14px 36px rgba(61, 57, 41, 0.12);
--shadow-soft: 0 12px 24px rgba(38, 28, 18, 0.12);
--shadow-mid: 0 20px 45px rgba(16, 24, 40, 0.08);
--shadow-strong: 0 28px 60px rgba(38, 28, 18, 0.25);
--shadow-color: rgba(0, 0, 0, 0.1);
/* Overlay / 移动端 */
--overlay-scrim: rgba(33, 24, 14, 0.55);
--mobile-menu: #f5f0e8;
--mobile-menu-shadow: 0 12px 30px rgba(38, 28, 18, 0.15);
/* 专用色 */
--diff-add-text: #00e000;
--diff-del-text: #ff1a1a;
--diff-add-bg: rgba(22, 163, 74, 0.12);
--diff-del-bg: rgba(220, 38, 38, 0.12);
}
:root[data-theme='light'] {
color-scheme: light;
/* 类似 ChatGPT 的明亮配色:白底黑字,优雅浅灰 */
--claude-bg: #ffffff;
--claude-panel: #ffffff;
--claude-left-rail: #f9f9f9;
--claude-sidebar: #ffffff;
--claude-border: rgba(0, 0, 0, 0.08);
--claude-border-strong: rgba(0, 0, 0, 0.12);
--claude-text: #0d0d0d;
--claude-text-secondary: #676767;
--claude-text-tertiary: #8e8e8e;
--claude-muted: rgba(0, 0, 0, 0.3);
--claude-accent: #acacac;
--claude-accent-strong: #8e8e8e;
--claude-deep: #c4c4c4;
--claude-deep-strong: #acacac;
--claude-highlight: rgba(0, 0, 0, 0.05);
--claude-button-hover: #d1d1d1;
--claude-button-active: #acacac;
--claude-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
--claude-success: #10b981;
--claude-warning: #f59e0b;
--claude-danger: #dc2626;
--git-diff-add-text: #00e000;
--git-diff-del-text: #ff1a1a;
--theme-surface-card: #ffffff;
--theme-surface-strong: #ffffff;
--theme-surface-soft: #ffffff;
--theme-surface-muted: #fafafa;
--theme-overlay-scrim: rgba(0, 0, 0, 0.5);
--theme-shadow-strong: 0 2px 4px rgba(0, 0, 0, 0.08);
--theme-shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.05);
--theme-shadow-mid: 0 1px 3px rgba(0, 0, 0, 0.06);
--theme-control-border: rgba(0, 0, 0, 0.08);
--theme-control-border-strong: rgba(0, 0, 0, 0.12);
--theme-switch-track: #ececec;
--theme-chip-bg: #f5f5f5;
--theme-chip-border: rgba(0, 0, 0, 0.08);
--theme-badge-bg: #f5f5f5;
--theme-tab-active: rgba(0, 0, 0, 0.05);
--theme-mobile-menu: rgba(255, 255, 255, 0.95);
--theme-mobile-menu-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
--theme-card-border-strong: rgba(0, 0, 0, 0.1);
/* Surface 表面层级 —— ChatGPT 亮色冷白盘 */
--surface-base: #ffffff;
--surface-panel: #ffffff;
--surface-rail: #f9f9f9;
--surface-sidebar: #f9f9f9;
--surface-card: #ffffff;
--surface-raised: #ffffff;
--surface-soft: #f9f9f9;
--surface-muted: #f3f3f3;
/* Text 文字 —— ChatGPT */
--text-primary: #0d0d0d;
--text-secondary: #5d5d5d;
--text-tertiary: #8f8f8f;
--text-muted: rgba(13, 13, 13, 0.35);
/* Border 边框 —— ChatGPT hairline */
--border-default: rgba(13, 13, 13, 0.1);
--border-strong: rgba(13, 13, 13, 0.15);
--border-card-strong: rgba(13, 13, 13, 0.1);
--chip-border: rgba(13, 13, 13, 0.05);
/* Accent 强调 —— ChatGPT primary近黑克制 CTA-only */
--accent: #181818;
--accent-strong: #181818;
--accent-hover: #303030;
--accent-active: #414141;
--accent-deep: #5d5d5d;
--accent-deep-strong: #414141;
--on-accent: #ffffff;
/* State 状态 —— ChatGPT semantic */
--state-success: #00a240;
--state-warning: #e25507;
--state-danger: #e02e2a;
--state-danger-strong: #911e1b;
--status-offline: #e02e2a;
--state-info: #0285ff;
/* Highlight / 交互态 —— 中性冷灰 */
--highlight: rgba(13, 13, 13, 0.05);
--tab-active: rgba(13, 13, 13, 0.05);
--chip-bg: #f3f3f3;
--badge-bg: #f3f3f3;
--switch-track: #ececec;
--hover-bg: rgba(13, 13, 13, 0.05);
--pulse-ring: rgba(0, 0, 0, 0.1);
/* Shadow 阴影 */
--shadow-card: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-mid: 0 1px 3px rgba(0, 0, 0, 0.06);
--shadow-strong: 0 2px 4px rgba(0, 0, 0, 0.08);
--shadow-color: rgba(0, 0, 0, 0.1);
/* Overlay / 移动端 */
--overlay-scrim: rgba(0, 0, 0, 0.5);
--mobile-menu: #ffffff;
--mobile-menu-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
/* 专用色 */
--diff-add-text: #00e000;
--diff-del-text: #ff1a1a;
--diff-add-bg: rgba(22, 163, 74, 0.12);
--diff-del-bg: rgba(220, 38, 38, 0.12);
}
:root[data-theme='dark'] {
color-scheme: dark;
/* 暗色主题:灰黑色底色,白色文字 */
--claude-bg: #1a1a1a;
--claude-panel: #1a1a1a;
--claude-left-rail: #181818;
--claude-sidebar: #181818;
--claude-border: rgba(255, 255, 255, 0.08);
--claude-border-strong: rgba(255, 255, 255, 0.12);
--claude-text: #ffffff;
--claude-text-secondary: #a0a0a0;
--claude-text-tertiary: #707070;
--claude-muted: rgba(255, 255, 255, 0.3);
--claude-accent: #606060;
--claude-accent-strong: #505050;
--claude-deep: #808080;
--claude-deep-strong: #606060;
--claude-highlight: rgba(255, 255, 255, 0.05);
--claude-button-hover: #707070;
--claude-button-active: #505050;
--claude-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
--claude-success: #10b981;
--claude-warning: #f59e0b;
--claude-danger: #dc2626;
--git-diff-add-text: #00e000;
--git-diff-del-text: #ff1a1a;
--theme-surface-card: #0a0a0a;
--theme-surface-strong: #1a1a1a;
--theme-surface-soft: #0f0f0f;
--theme-surface-muted: #141414;
--theme-overlay-scrim: rgba(0, 0, 0, 0.8);
--theme-shadow-strong: 0 4px 12px rgba(0, 0, 0, 0.6);
--theme-shadow-soft: 0 2px 6px rgba(0, 0, 0, 0.4);
--theme-shadow-mid: 0 3px 8px rgba(0, 0, 0, 0.5);
--theme-control-border: rgba(255, 255, 255, 0.08);
--theme-control-border-strong: rgba(255, 255, 255, 0.12);
--theme-switch-track: #2a2a2a;
--theme-chip-bg: #2a2a2a;
--theme-chip-border: rgba(255, 255, 255, 0.08);
--theme-badge-bg: #2a2a2a;
--theme-tab-active: rgba(255, 255, 255, 0.05);
--theme-mobile-menu: rgba(10, 10, 10, 0.95);
--theme-mobile-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
--theme-card-border-strong: rgba(255, 255, 255, 0.1);
/* Surface 表面层级 */
--surface-base: #1a1a1a;
--surface-panel: #1a1a1a;
--surface-rail: #181818;
--surface-sidebar: #181818;
--surface-card: #0a0a0a;
--surface-raised: #1a1a1a;
--surface-soft: #0f0f0f;
--surface-muted: #141414;
/* Text 文字 */
--text-primary: #ffffff;
--text-secondary: #a0a0a0;
--text-tertiary: #707070;
--text-muted: rgba(255, 255, 255, 0.3);
/* Border 边框 */
--border-default: rgba(255, 255, 255, 0.08);
--border-strong: rgba(255, 255, 255, 0.12);
--border-card-strong: rgba(255, 255, 255, 0.1);
--chip-border: rgba(255, 255, 255, 0.08);
/* Accent 强调(含运行模式 deep */
--accent: #606060;
--accent-strong: #505050;
--accent-hover: #707070;
--accent-active: #505050;
--accent-deep: #808080;
--accent-deep-strong: #606060;
--on-accent: #ffffff;
/* State 状态 */
--state-success: #10b981;
--state-warning: #f59e0b;
--state-danger: #dc2626;
--state-danger-strong: #b91c1c;
--status-offline: #e34d4d;
--state-info: #60a5fa;
/* Highlight / 交互态 */
--highlight: rgba(255, 255, 255, 0.05);
--tab-active: rgba(255, 255, 255, 0.05);
--chip-bg: #2a2a2a;
--badge-bg: #2a2a2a;
--switch-track: #2a2a2a;
--hover-bg: rgba(255, 255, 255, 0.06);
--pulse-ring: rgba(255, 255, 255, 0.1);
/* Shadow 阴影 */
--shadow-card: 0 2px 8px rgba(0, 0, 0, 0.5);
--shadow-soft: 0 2px 6px rgba(0, 0, 0, 0.4);
--shadow-mid: 0 3px 8px rgba(0, 0, 0, 0.5);
--shadow-strong: 0 4px 12px rgba(0, 0, 0, 0.6);
--shadow-color: rgba(0, 0, 0, 0.4);
/* Overlay / 移动端 */
--overlay-scrim: rgba(0, 0, 0, 0.8);
--mobile-menu: rgba(10, 10, 10, 0.95);
--mobile-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
/* 专用色 */
--diff-add-text: #00e000;
--diff-del-text: #ff1a1a;
--diff-add-bg: rgba(22, 163, 74, 0.12);
--diff-del-bg: rgba(220, 38, 38, 0.12);
}
/* 首屏JS 设置 data-theme 之前)回退到 classic 取值 */
:root:not([data-theme]) {
/* Surface 表面层级 —— 同 classicClaude 亮色暖奶油盘) */
--surface-base: #faf9f5;
--surface-panel: #faf9f5;
--surface-rail: #f5f0e8;
--surface-sidebar: #f5f0e8;
--surface-card: #efe9de;
--surface-raised: #ffffff;
--surface-soft: #f5f0e8;
--surface-muted: #e8e0d2;
/* Text 文字 */
--text-primary: #141413;
--text-secondary: #3d3d3a;
--text-tertiary: #6c6a64;
--text-muted: rgba(108, 106, 100, 0.55);
/* Border 边框 */
--border-default: #e6dfd8;
--border-strong: #d8cfc4;
--border-card-strong: #e6dfd8;
--chip-border: #ebe6df;
/* Accent 强调 */
--accent: #cc785c;
--accent-strong: #a9583e;
--accent-hover: #bd6a4f;
--accent-active: #a9583e;
--accent-deep: #6c6a64;
--accent-deep-strong: #3d3d3a;
--on-accent: #ffffff;
/* State 状态 */
--state-success: #5db872;
--state-warning: #d4a017;
--state-danger: #c64545;
--state-danger-strong: #a83838;
--status-offline: #c64545;
--state-info: #5db8a6;
/* Highlight / 交互态 —— 中性暖灰 */
--highlight: rgba(20, 20, 19, 0.05);
--tab-active: rgba(20, 20, 19, 0.07);
--chip-bg: #f5f0e8;
--badge-bg: #efe9de;
--switch-track: #d8cfc4;
--hover-bg: rgba(20, 20, 19, 0.05);
--pulse-ring: rgba(0, 0, 0, 0.1);
/* Shadow 阴影 */
--shadow-card: 0 14px 36px rgba(61, 57, 41, 0.12);
--shadow-soft: 0 12px 24px rgba(38, 28, 18, 0.12);
--shadow-mid: 0 20px 45px rgba(16, 24, 40, 0.08);
--shadow-strong: 0 28px 60px rgba(38, 28, 18, 0.25);
--shadow-color: rgba(0, 0, 0, 0.1);
/* Overlay / 移动端 */
--overlay-scrim: rgba(33, 24, 14, 0.55);
--mobile-menu: #f5f0e8;
--mobile-menu-shadow: 0 12px 30px rgba(38, 28, 18, 0.15);
/* 专用色 */
--diff-add-text: #00e000;
--diff-del-text: #ff1a1a;
--diff-add-bg: rgba(22, 163, 74, 0.12);
--diff-del-bg: rgba(220, 38, 38, 0.12);
}

View File

@ -1,7 +1,16 @@
/* 聊天容器整体布局,保证聊天区可见并支持上下滚动 */
.chat-container {
--chat-surface-color: var(--theme-surface-strong, #ffffff);
--composer-base-height: calc(80px + var(--app-bottom-inset, 0px));
--chat-surface-color: var(--surface-panel);
--chat-rail-max: 900px;
--chat-rail-fluid: 94%;
--chat-scrollbar-width: 8px;
--chat-rail-width: min(var(--chat-rail-max), var(--chat-rail-fluid));
--chat-message-rail-width: min(
var(--chat-rail-max),
calc(var(--chat-rail-fluid) + (var(--chat-scrollbar-width) * 0.94))
);
--chat-content-x: 0px;
--composer-base-height: calc(90px + var(--app-bottom-inset));
--composer-reserved-height: var(--composer-base-height);
--composer-growth-height: 0px;
flex: 1;
@ -9,7 +18,7 @@
flex-direction: column;
background: var(--chat-surface-color);
min-width: 0;
height: var(--app-viewport, 100vh);
height: var(--app-viewport);
overflow: hidden;
position: relative;
backdrop-filter: blur(6px);
@ -23,7 +32,7 @@
display: flex;
align-items: center;
justify-content: space-between;
color: #0f172a;
color: var(--text-primary);
background: var(--chat-surface-color); /* 与对话区域保持一致且不透明 */
box-shadow: none;
pointer-events: auto;
@ -65,8 +74,8 @@
letter-spacing: 0.02em;
font-family: 'Iowan Old Style', ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif;
max-width: 60%;
color: #0f172a;
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
color: var(--text-primary);
text-shadow: 0 1px 6px var(--shadow-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -98,7 +107,9 @@
}
.messages-flow {
width: min(960px, 100%);
position: relative;
left: calc(var(--chat-scrollbar-width) / 2);
width: var(--chat-message-rail-width);
margin: 0 auto;
display: flex;
flex-direction: column;
@ -134,7 +145,7 @@
.messages-area::-webkit-scrollbar-thumb,
.sidebar::-webkit-scrollbar-thumb,
.conversation-list::-webkit-scrollbar-thumb {
background: rgba(121, 109, 94, 0.4);
background: var(--text-muted);
border-radius: 8px;
}
@ -161,21 +172,24 @@
transform: translateY(8px);
}
.chat-container--immersive .messages-flow {
width: min(900px, 100%);
}
.chat-container--immersive .scroll-lock-toggle {
right: 8px;
}
.chat-container--mobile {
--chat-rail-fluid: 100%;
padding-top: 52px;
}
.chat-container--mobile.chat-container--app-shell {
--chat-scrollbar-width: 0px !important;
--chat-message-rail-width: var(--chat-rail-width) !important;
}
@media (max-width: 768px) {
.chat-container {
--composer-base-height: calc(80px + var(--app-bottom-inset, 0px));
--chat-rail-fluid: 100%;
--composer-base-height: calc(90px + var(--app-bottom-inset));
--composer-reserved-height: var(--composer-base-height);
}
@ -205,6 +219,7 @@
/* 额外的手机端样式,不依赖 mobile 类 */
@media (max-width: 768px) {
.messages-flow {
left: 0;
padding-left: 0;
padding-right: 0;
}
@ -220,7 +235,7 @@
border: none;
background: transparent;
box-shadow: none;
color: #0f172a;
color: var(--text-primary);
font-weight: 600;
font-size: 15px;
line-height: 1.35;
@ -230,7 +245,7 @@
}
.conversation-ribbon__selector:hover {
color: #111827;
color: var(--text-primary);
}
.conversation-ribbon__selector:disabled {
@ -239,7 +254,7 @@
}
.conversation-ribbon__selector.open {
color: #111827;
color: var(--text-primary);
}
.selector-label {
@ -258,7 +273,7 @@
.selector-mode {
font-weight: 500;
color: rgba(15, 23, 42, 0.8);
color: var(--text-secondary);
}
.selector-caret {
@ -276,9 +291,8 @@
gap: 12px;
padding: 12px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.96);
background: var(--surface-soft);
box-shadow: none;
backdrop-filter: blur(8px);
pointer-events: auto;
width: min(640px, calc(100vw - 32px));
max-width: calc(100vw - 32px);
@ -339,7 +353,7 @@
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: rgba(15, 23, 42, 0.65);
color: var(--text-secondary);
padding: 2px 12px;
}
@ -354,11 +368,11 @@
gap: 6px;
cursor: pointer;
transition: all 0.18s ease;
color: #0f172a;
color: var(--text-primary);
}
.dropdown-item:hover:not(.disabled) {
background: #f6f7fb;
background: var(--hover-bg);
}
.dropdown-item.disabled {
@ -374,13 +388,13 @@
.item-desc {
grid-column: 1 / 2;
font-size: 12px;
color: rgba(15, 23, 42, 0.65);
color: var(--text-secondary);
}
.item-check {
align-self: center;
font-weight: 800;
color: #0f172a;
color: var(--text-primary);
}
.header-menu-enter-active,
@ -402,7 +416,7 @@
height: 36px;
border-radius: 50%;
border: 1px solid var(--claude-border);
background: rgba(255, 255, 255, 0.92);
background: var(--surface-soft);
display: flex;
align-items: center;
justify-content: center;
@ -499,7 +513,7 @@
}
.user-message .message-text {
background: #f5f5f5;
background: var(--chip-bg);
display: flex;
flex-direction: column;
gap: 10px;
@ -529,7 +543,7 @@
display: flex;
align-items: center;
gap: 14px;
width: min(760px, 92%);
width: 100%;
margin: 2px auto;
color: var(--claude-text-secondary);
user-select: none;
@ -540,7 +554,7 @@
content: '';
flex: 1 1 auto;
height: 0;
border-top: 1px solid var(--theme-control-border, var(--claude-border));
border-top: 1px solid var(--theme-control-border);
}
.user-message.user-message--brief .compact-brief-text {
@ -552,17 +566,12 @@
}
.user-message .message-text .user-skill-link {
color: #2563eb;
color: var(--state-info);
font-weight: 600;
display: inline-block;
margin: 0 0.25em;
}
:root[data-theme='dark'] .user-message .message-text .user-skill-link,
body[data-theme='dark'] .user-message .message-text .user-skill-link {
color: #60a5fa;
}
.assistant-message .message-text {
background: var(--claude-highlight);
border-left: 4px solid var(--claude-accent);
@ -583,7 +592,7 @@ body[data-theme='dark'] .user-message .message-text .user-skill-link {
border-radius: 6px;
overflow: hidden;
cursor: pointer;
border: 1px solid var(--border-color, #2a2f3a);
border: 1px solid var(--border-default);
}
.user-message .message-text.user-bubble-text .image-thumbnail {
@ -651,7 +660,7 @@ body[data-theme='dark'] .user-message .message-text .user-skill-link {
}
.collapsible-block {
background: rgba(255, 255, 255, 0.78);
background: transparent;
border-radius: 12px;
margin-bottom: 12px;
overflow: hidden;
@ -667,7 +676,7 @@ body[data-theme='dark'] .user-message .message-text .user-skill-link {
align-items: center;
gap: 8px;
user-select: none;
background: rgba(255, 255, 255, 0.72);
background: transparent;
transition: background-color 0.2s ease;
position: relative;
min-height: 36px;
@ -1184,12 +1193,17 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
}
.text-output .text-content {
padding: 0 20px 0 15px;
padding: 0 var(--chat-content-x);
max-width: 100%;
min-width: 0;
overflow-x: hidden;
}
.text-output .text-content .code-block-wrapper {
margin-right: calc(var(--chat-content-x) * -1);
margin-left: calc(var(--chat-content-x) * -1);
}
.text-output pre {
font-family:
'JetBrains Mono', 'SF Mono', 'Fira Code', 'Consolas', 'Menlo', 'Monaco', 'Courier New',
@ -1229,9 +1243,9 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
.text-output .text-content .md-table-scroll,
.text-output .text-content [data-md-table-scroll='1'] {
display: block;
width: 100%;
max-width: 100%;
margin: 16px 0;
width: auto;
max-width: none;
margin: 16px calc(var(--chat-content-x) * -1);
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
@ -1253,21 +1267,21 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
.text-output .text-content .md-table-scroll::-webkit-scrollbar-track,
.text-output .text-content [data-md-table-scroll='1']::-webkit-scrollbar-track {
background: color-mix(in srgb, var(--theme-surface-muted, #d1d5db) 70%, transparent);
background: color-mix(in srgb, var(--theme-surface-muted) 70%, transparent);
border-radius: 999px;
margin: 0 8px 6px;
}
.text-output .text-content .md-table-scroll::-webkit-scrollbar-thumb,
.text-output .text-content [data-md-table-scroll='1']::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--claude-text-secondary, #6b7280) 55%, transparent);
background: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent);
border-radius: 999px;
}
.text-output .text-content .md-table-scroll,
.text-output .text-content [data-md-table-scroll='1'] {
scrollbar-color: color-mix(in srgb, var(--claude-text-secondary, #6b7280) 55%, transparent)
color-mix(in srgb, var(--theme-surface-muted, #d1d5db) 70%, transparent);
scrollbar-color: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent)
color-mix(in srgb, var(--theme-surface-muted) 70%, transparent);
scrollbar-width: thin;
}
@ -1290,7 +1304,7 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
}
.text-output .text-content hr {
margin: 32px 0;
margin: 32px calc(var(--chat-content-x) * -1);
border: none;
border-top: 1px solid var(--claude-border);
}
@ -1299,8 +1313,8 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
margin: 12px 0;
padding: 10px 14px;
border-radius: 8px;
background: linear-gradient(135deg, rgba(99, 102, 241, 0.08), rgba(59, 130, 246, 0.08));
border-left: 4px solid rgba(79, 70, 229, 0.6);
background: linear-gradient(135deg, color-mix(in srgb, var(--state-info) 8%, transparent), color-mix(in srgb, var(--state-info) 8%, transparent));
border-left: 4px solid var(--state-info);
color: var(--claude-text);
font-size: 14px;
line-height: 1.5;
@ -1336,8 +1350,8 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
.append-block.append-error,
.append-placeholder.append-error {
background: rgba(255, 244, 242, 0.85);
border-left-color: rgba(216, 90, 66, 0.38);
background: color-mix(in srgb, var(--state-danger) 8%, var(--surface-soft));
border-left-color: color-mix(in srgb, var(--state-danger) 40%, transparent);
}
.append-header {
@ -1352,14 +1366,14 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
border-radius: 20px;
overflow: hidden;
margin: 16px 0;
background: var(--theme-surface-strong, #ffffff);
background: var(--theme-surface-strong);
min-height: 80px;
box-shadow: none;
}
.code-block-header {
min-height: 44px;
background: var(--theme-surface-strong, #ffffff);
background: var(--theme-surface-strong);
padding: 0 12px 0 18px;
display: flex;
justify-content: space-between;
@ -1404,7 +1418,7 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
}
.copy-code-btn:hover {
background-color: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background-color: var(--theme-tab-active);
color: var(--claude-text);
}
@ -1414,7 +1428,7 @@ show-html:not([data-rendered='1'])[ratio='3:4'] {
}
.code-block-wrapper pre {
background: var(--theme-surface-strong, #ffffff) !important;
background: var(--theme-surface-strong) !important;
padding: 24px 28px 28px !important;
margin: 0 !important;
border-radius: 0 !important;

View File

@ -47,10 +47,10 @@
color: var(--claude-text-secondary);
}
// 动画 - 使用主题色的脉冲效果
// 动画 - 使用主题色的脉冲效果颜色走 --pulse-ring随主题自动适配
@keyframes drag-pulse {
0%, 100% {
box-shadow: 0 0 0 0 var(--claude-highlight);
box-shadow: 0 0 0 0 var(--pulse-ring);
}
50% {
box-shadow: 0 0 0 12px transparent;
@ -82,15 +82,6 @@
.drag-upload-hint {
color: var(--claude-text);
}
@keyframes drag-pulse {
0%, 100% {
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.1);
}
50% {
box-shadow: 0 0 0 12px transparent;
}
}
}
// 经典色主题(classic) - 提示文字改为黑色
@ -111,13 +102,4 @@
.drag-upload-icon {
color: var(--claude-text);
}
@keyframes drag-pulse {
0%, 100% {
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.1);
}
50% {
box-shadow: 0 0 0 12px transparent;
}
}
}

View File

@ -20,7 +20,7 @@
.stadium-input-wrapper {
position: relative;
width: min(900px, 94%);
width: var(--chat-rail-width, min(900px, 94%));
pointer-events: auto;
}
@ -29,7 +29,7 @@
--runtime-queue-divider: rgba(15, 23, 42, 0.12);
--runtime-queue-border: rgba(15, 23, 42, 0.12);
position: absolute;
left: calc(50% - 7px);
left: 50%;
bottom: calc(100% - 2px);
transform: translateX(-50%);
z-index: 1;
@ -113,8 +113,8 @@
}
.runtime-queue-item__action--danger:hover {
color: #b91c1c;
background: rgba(185, 28, 28, 0.12);
color: var(--state-danger-strong);
background: color-mix(in srgb, var(--state-danger) 12%, transparent);
}
.runtime-queue-item + .runtime-queue-item::before {
@ -137,7 +137,7 @@
--skill-slash-motion-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
--skill-slash-visible-rows: 5;
position: absolute;
left: calc(50% - 7px);
left: 50%;
bottom: calc(100% - 2px);
transform: translateX(-50%);
z-index: 1;
@ -153,7 +153,7 @@
border: 1px solid var(--claude-border);
border-top-left-radius: var(--skill-slash-radius);
border-top-right-radius: var(--skill-slash-radius);
background: rgba(255, 255, 255, 0.98);
background: var(--surface-soft);
box-shadow: none;
pointer-events: auto;
scrollbar-width: none;
@ -227,8 +227,8 @@
.skill-slash-item:hover,
.skill-slash-item--active {
background: rgba(118, 103, 84, 0.12);
box-shadow: 0 1px 2px rgba(61, 57, 41, 0.08);
background: var(--hover-bg);
box-shadow: 0 1px 2px var(--shadow-color);
}
.skill-slash-item--disabled,
@ -246,7 +246,7 @@
.skill-slash-item__name {
flex: 0 0 auto;
max-width: 210px;
color: #2563eb;
color: var(--accent);
font-size: 13px;
font-weight: 600;
white-space: nowrap;
@ -277,7 +277,7 @@
body[data-theme='dark'] {
.runtime-queue-list {
--runtime-queue-bg: #2a2a2a;
--runtime-queue-bg: var(--badge-bg);
--runtime-queue-divider: rgba(255, 255, 255, 0.12);
--runtime-queue-border: rgba(255, 255, 255, 0.12);
background: transparent;
@ -294,23 +294,34 @@ body[data-theme='dark'] {
}
.skill-slash-menu {
background: #2a2a2a;
background: var(--badge-bg);
border-color: var(--claude-border);
}
.skill-slash-item__name {
color: #60a5fa;
color: var(--state-info);
}
.skill-slash-item:hover,
.skill-slash-item--active {
background: rgba(255, 255, 255, 0.078);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.28);
background: var(--hover-bg);
box-shadow: 0 1px 2px var(--shadow-color);
}
.stadium-input-editor .skill-md-link,
.stadium-input-highlight .skill-md-link {
color: #60a5fa;
color: var(--state-info);
}
}
body[data-theme='light'] {
.skill-slash-item__name {
color: var(--state-info);
}
.stadium-input-editor .skill-md-link,
.stadium-input-highlight .skill-md-link {
color: var(--state-info);
}
}
@ -365,7 +376,7 @@ body[data-theme='dark'] {
height: 18px;
border-radius: 50%;
background:
conic-gradient(var(--context-ring-color) var(--context-progress), rgba(127, 135, 146, 0.28) 0);
conic-gradient(var(--context-ring-color) var(--context-progress), var(--progress-track) 0);
display: inline-flex;
align-items: center;
justify-content: center;
@ -511,7 +522,7 @@ body[data-theme='dark'] {
}
.permission-switcher__item-label--warn {
color: #d97706;
color: var(--state-warning);
}
@media (max-width: 768px) {
@ -540,15 +551,14 @@ body[data-theme='dark'] {
}
.stadium-shell {
--stadium-radius: 24px;
--stadium-radius: 18px;
position: relative;
z-index: 2;
width: 100%;
min-height: calc(var(--stadium-radius) * 2.1);
padding: 12px 18px;
padding: 10px;
border-radius: var(--stadium-radius);
border: 1px solid rgba(15, 23, 42, 0.12);
background: #ffffff;
border: 1px solid var(--border-default);
background: var(--surface-raised);
box-shadow: none;
display: flex;
gap: 12px;
@ -558,22 +568,19 @@ body[data-theme='dark'] {
}
.stadium-shell.is-multiline {
padding-top: 16px;
padding-bottom: 16px;
min-height: calc(var(--stadium-radius) * 2.7);
border-color: rgba(15, 23, 42, 0.2);
border-color: var(--border-strong);
box-shadow: none;
}
.stadium-shell.is-focused,
.stadium-shell.has-text {
border-color: rgba(15, 23, 42, 0.12);
border-color: var(--border-default);
box-shadow: none;
}
.stadium-shell.is-multiline.is-focused,
.stadium-shell.is-multiline.has-text {
border-color: rgba(15, 23, 42, 0.2);
border-color: var(--border-strong);
box-shadow: none;
}
@ -586,16 +593,24 @@ body[data-theme='dark'] {
.input-row {
display: flex;
align-items: center;
align-items: flex-start;
gap: 12px;
width: 100%;
}
.input-actions {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.stadium-input-rich {
position: relative;
flex: 1 1 auto;
min-width: 0;
display: grid;
padding: 2px 0 0 6px;
}
.stadium-input-rich .stadium-input {
@ -603,7 +618,7 @@ body[data-theme='dark'] {
}
.stadium-input-editor .skill-md-link {
color: #2563eb;
color: var(--accent);
font-weight: inherit;
border-radius: 4px;
padding: 0 1px;
@ -624,7 +639,7 @@ body[data-theme='dark'] {
gap: 6px;
padding: 0 4px 0;
font-size: 12px;
color: var(--text-secondary, #7f8792);
color: var(--text-secondary);
line-height: 1.4;
}
@ -638,7 +653,7 @@ body[data-theme='dark'] {
.image-remove-btn {
border: none;
background: transparent;
color: var(--text-secondary, #7f8792);
color: var(--text-secondary);
cursor: pointer;
padding: 0 4px;
font-size: 12px;
@ -646,7 +661,7 @@ body[data-theme='dark'] {
transition: color 0.15s ease, transform 0.15s ease;
}
.image-remove-btn:hover {
color: #d14b4b;
color: var(--state-danger);
transform: scale(1.05);
}
@ -713,19 +728,19 @@ body[data-theme='dark'] {
}
.stadium-btn {
flex: 0 0 36px;
width: 36px;
height: 36px;
flex: 0 0 28px;
width: 28px;
height: 28px;
border: none;
border-radius: 50%;
border-radius: 7px;
background: transparent;
color: var(--claude-text);
font-size: 18px;
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s ease, transform 0.2s ease, margin-top 0.2s ease;
transition: background 140ms ease, color 140ms ease;
}
.stadium-btn:disabled {
@ -734,21 +749,20 @@ body[data-theme='dark'] {
}
.stadium-btn:hover:not(:disabled) {
background: rgba(0, 0, 0, 0.05);
background: var(--hover-bg);
}
.add-btn {
font-size: 22px;
font-size: 20px;
}
.stadium-btn.send-btn {
background: var(--claude-accent);
color: #fffaf0;
box-shadow: 0 10px 20px rgba(189, 93, 58, 0.28);
color: var(--on-accent);
box-shadow: none;
}
.stadium-btn.send-btn:hover:not(:disabled) {
transform: translateY(-1px);
background: var(--claude-button-hover);
}
@ -765,31 +779,26 @@ body[data-theme='dark'] {
.stadium-btn.send-btn .send-icon {
width: 0;
height: 0;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-left: 10px solid #fffaf0;
margin-left: 5px;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 9px solid var(--on-accent);
margin-left: 4px;
}
.stadium-btn.send-btn .stop-icon {
width: 12px;
height: 12px;
width: 11px;
height: 11px;
border-radius: 2px;
background-color: #fffaf0;
background-color: var(--on-accent);
display: block;
}
.stadium-btn.send-btn:disabled .send-icon {
border-left-color: rgba(255, 255, 255, 0.4);
border-left-color: color-mix(in srgb, var(--on-accent) 40%, transparent);
}
.stadium-btn.send-btn:disabled .stop-icon {
background-color: rgba(255, 255, 255, 0.4);
}
.stadium-shell.is-multiline .stadium-btn {
align-self: flex-end;
margin-top: 0;
background-color: color-mix(in srgb, var(--on-accent) 40%, transparent);
}
.file-input-hidden {
@ -805,7 +814,7 @@ body[data-theme='dark'] {
gap: 6px;
width: 230px;
padding: 12px;
background: rgba(255, 255, 255, 0.98);
background: var(--surface-soft);
border: 1px solid var(--claude-border);
border-radius: 18px;
box-shadow: none;
@ -838,11 +847,11 @@ body[data-theme='dark'] {
}
.menu-entry:hover:not(:disabled) {
background: rgba(0, 0, 0, 0.05);
background: var(--hover-bg);
}
.menu-entry.active {
background: rgba(118, 103, 84, 0.12);
background: var(--chip-bg);
color: var(--claude-text);
font-weight: 600;
}
@ -866,7 +875,7 @@ body[data-theme='dark'] {
padding: 12px;
border-radius: 18px;
border: 1px solid var(--claude-border);
background: rgba(255, 255, 255, 0.98);
background: var(--surface-soft);
box-shadow: none;
z-index: 31;
}
@ -898,12 +907,12 @@ body[data-theme='dark'] {
pointer-events: none;
z-index: 1;
gap: 10px;
padding-bottom: 140px;
padding-bottom: 160px;
}
.blank-hero-text {
font-size: 32px;
color: #2f3a4a;
color: var(--text-primary);
font-weight: 600;
margin: 0;
max-width: min(88vw, 680px);
@ -919,8 +928,8 @@ body[data-theme='dark'] {
.composer-container {
position: relative;
height: var(--composer-base-height, calc(80px + var(--app-bottom-inset, 0px)));
flex: 0 0 var(--composer-base-height, calc(80px + var(--app-bottom-inset, 0px)));
height: var(--composer-base-height, calc(90px + var(--app-bottom-inset)));
flex: 0 0 var(--composer-base-height, calc(90px + var(--app-bottom-inset)));
transition: transform 0.3s ease;
z-index: 2;
}
@ -933,6 +942,10 @@ body[data-theme='dark'] {
}
@media (max-width: 768px) {
.input-area {
padding: 0 12px;
}
.blank-hero-text {
font-size: 28px;
}
@ -944,7 +957,7 @@ body[data-theme='dark'] {
transform: translateY(calc(-32vh + var(--composer-growth-height, 0px)));
}
.blank-hero-overlay {
padding-bottom: 120px;
padding-bottom: 136px;
}
}
@ -1015,10 +1028,6 @@ body[data-theme='dark'] {
@media (max-height: 900px) {
.input-area {
bottom: 12px;
padding: 0 12px;
}
.stadium-shell {
width: min(900px, 98%);
}
}
@ -1026,7 +1035,7 @@ body[data-theme='dark'] {
position: absolute;
right: 0;
bottom: calc(100% + 12px);
background: rgba(255, 255, 255, 0.96);
background: var(--surface-soft);
border: 1px solid var(--claude-border);
border-radius: 12px;
box-shadow: none;
@ -1045,7 +1054,7 @@ body[data-theme='dark'] {
right: 20px;
border-width: 10px 10px 0 10px;
border-style: solid;
border-color: rgba(255, 255, 255, 0.96) transparent transparent transparent;
border-color: var(--surface-soft) transparent transparent transparent;
filter: drop-shadow(0 3px 4px rgba(61, 57, 41, 0.12));
}
@ -1065,7 +1074,7 @@ body[data-theme='dark'] {
.tool-menu .tool-menu-status,
.tool-menu .tool-menu-empty {
font-size: 13px;
color: rgba(61, 57, 41, 0.78);
color: var(--text-secondary);
text-align: left;
}
@ -1081,9 +1090,9 @@ body[data-theme='dark'] {
align-items: center;
gap: 10px;
padding: 14px 10px;
border: 1px solid rgba(118, 103, 84, 0.14);
border: 1px solid var(--border-default);
border-radius: 10px;
background: rgba(255, 255, 255, 0.88);
background: var(--surface-soft);
font-size: 13px;
aspect-ratio: 1 / 1;
min-height: 0;
@ -1127,19 +1136,19 @@ body[data-theme='dark'] {
.menu-btn {
width: 100%;
padding: 8px 14px;
border: 1px solid rgba(118, 103, 84, 0.14);
border: 1px solid var(--border-default);
border-radius: 8px;
font-size: 13px;
font-weight: 500;
text-align: left;
background: rgba(255, 255, 255, 0.78);
background: var(--surface-soft);
color: var(--claude-text);
cursor: pointer;
transition: all 0.2s ease;
}
.menu-btn:not(:disabled):hover {
background: rgba(255, 255, 255, 0.95);
background: var(--surface-soft);
transform: translateY(-1px);
}
@ -1149,21 +1158,21 @@ body[data-theme='dark'] {
}
.menu-btn.compress-entry {
background: rgba(255, 255, 255, 0.78);
background: var(--surface-soft);
color: var(--claude-success);
}
.menu-btn.compress-entry:not(:disabled):hover {
background: rgba(255, 255, 255, 0.95);
background: var(--surface-soft);
}
.menu-btn.clear-entry {
background: rgba(255, 255, 255, 0.78);
color: #bf422b;
background: var(--surface-soft);
color: var(--state-danger);
}
.menu-btn.clear-entry:not(:disabled):hover {
background: rgba(255, 255, 255, 0.95);
background: var(--surface-soft);
}
.settings-menu-enter-active,
@ -1198,7 +1207,7 @@ body[data-theme='dark'] {
.mobile-panel-sheet--approval .approval-btn--approve {
background: var(--claude-accent);
color: #fff;
color: var(--on-accent);
border-color: var(--claude-accent);
}
@ -1253,10 +1262,10 @@ body[data-theme='dark'] {
min-width: 18px;
height: 18px;
padding: 0 5px;
border: 1.5px solid var(--theme-surface-strong, #fff);
border: 1.5px solid var(--theme-surface-strong);
border-radius: 999px;
background: #ff5f57;
color: #fff;
background: var(--mac-close);
color: var(--on-accent);
cursor: pointer;
font-size: 11px;
line-height: 15px;

File diff suppressed because it is too large Load Diff

View File

@ -56,6 +56,10 @@
.workspace-panel--collapsed {
pointer-events: none;
flex-basis: 0;
width: 0;
min-width: 0;
border: none;
}
.workspace-panel--collapsed > * {
@ -135,7 +139,6 @@
.mode-indicator {
--mode-indicator-color-1: var(--claude-accent);
--mode-indicator-color-2: var(--claude-accent-strong);
--mode-indicator-shadow: rgba(189, 93, 58, 0.25);
border: none;
cursor: pointer;
outline: none;
@ -147,8 +150,8 @@
align-items: center;
justify-content: center;
background: linear-gradient(135deg, var(--mode-indicator-color-1), var(--mode-indicator-color-2));
color: #fffef8;
box-shadow: 0 8px 20px var(--mode-indicator-shadow);
color: var(--on-accent);
box-shadow: none;
transition:
background 0.35s ease,
box-shadow 0.35s ease,
@ -160,27 +163,22 @@
}
.mode-indicator:focus-visible {
box-shadow:
0 0 0 2px rgba(255, 255, 255, 0.8),
0 8px 20px var(--mode-indicator-shadow);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--on-accent) 80%, transparent);
}
.mode-indicator.fast {
--mode-indicator-color-1: #ffe08c;
--mode-indicator-color-2: #f7b23c;
--mode-indicator-shadow: rgba(247, 178, 60, 0.35);
--mode-indicator-color-1: var(--accent-deep);
--mode-indicator-color-2: var(--accent-deep-strong);
}
.mode-indicator.thinking {
--mode-indicator-color-1: var(--claude-accent);
--mode-indicator-color-2: var(--claude-accent-strong);
--mode-indicator-shadow: rgba(189, 93, 58, 0.3);
}
.mode-indicator.deep {
--mode-indicator-color-1: var(--claude-deep);
--mode-indicator-color-2: var(--claude-deep-strong);
--mode-indicator-shadow: rgba(208, 122, 20, 0.35);
}
.mode-indicator .icon {
@ -192,7 +190,7 @@
width: 12px;
height: 12px;
border-radius: 6px;
background: #e34d4d;
background: var(--status-offline);
transition:
background 0.2s ease,
box-shadow 0.2s ease;
@ -205,7 +203,7 @@
position: absolute;
inset: 0;
border-radius: inherit;
background: #e34d4d;
background: var(--status-offline);
animation: statusPulse 2.4s ease-out infinite;
}
@ -266,8 +264,8 @@
}
.sidebar-manage-btn {
border: 1px solid rgba(118, 103, 84, 0.25);
background: rgba(255, 255, 255, 0.75);
border: 1px solid var(--border-default);
background: var(--surface-soft);
color: var(--claude-text);
padding: 4px 10px;
border-radius: 6px;
@ -279,16 +277,16 @@
}
.sidebar-manage-btn:hover {
background: rgba(255, 255, 255, 0.95);
border-color: rgba(118, 103, 84, 0.45);
background: var(--surface-soft);
border-color: var(--border-strong);
}
.sidebar-view-toggle {
width: 32px;
height: 32px;
border-radius: 8px;
border: 1px solid rgba(118, 103, 84, 0.3);
background: rgba(255, 255, 255, 0.85);
border: 1px solid var(--border-strong);
background: var(--surface-soft);
cursor: pointer;
display: flex;
align-items: center;
@ -299,7 +297,7 @@
}
.sidebar-view-toggle:hover {
background: rgba(255, 255, 255, 0.95);
background: var(--surface-soft);
}
.sidebar-panel-card-wrapper {
@ -349,8 +347,8 @@
top: 0;
display: flex;
gap: 6px;
background: rgba(255, 255, 255, 0.95);
border: 1px solid rgba(118, 103, 84, 0.2);
background: var(--surface-soft);
border: 1px solid var(--border-default);
border-radius: 8px;
padding: 6px 8px;
box-shadow: none;
@ -367,7 +365,7 @@
}
.panel-menu button.active {
background: rgba(108, 92, 231, 0.1);
background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.fade-enter-active,
@ -455,7 +453,7 @@
}
.sub-agent-card {
border: 1px solid rgba(118, 103, 84, 0.2);
border: 1px solid var(--border-default);
border-radius: 10px;
padding: 12px 14px;
background: color-mix(in srgb, var(--claude-left-rail) 88%, var(--theme-surface-strong) 12%);
@ -465,17 +463,17 @@
box-shadow 0.2s ease;
[data-theme='dark'] & {
background: rgba(30, 30, 30, 0.6);
border-color: rgba(255, 255, 255, 0.1);
background: color-mix(in srgb, var(--surface-base) 60%, transparent);
border-color: var(--border-card-strong);
}
}
.sub-agent-card:hover {
border-color: #6c5ce7;
border-color: var(--accent);
box-shadow: none;
[data-theme='dark'] & {
border-color: #8b7ce7;
border-color: var(--accent-hover);
box-shadow: none;
}
}
@ -494,18 +492,18 @@
}
.sub-agent-status.running {
color: #0984e3;
color: var(--state-info);
}
.sub-agent-status.completed {
color: #00b894;
color: var(--state-success);
}
.sub-agent-status.failed,
.sub-agent-status.timeout,
.sub-agent-status.terminated,
.sub-agent-status.cancelled {
color: #d63031;
color: var(--state-danger);
}
.sub-agent-summary {
@ -537,8 +535,8 @@
text-align: center;
[data-theme='dark'] & {
background: rgba(20, 20, 20, 0.9);
border-color: rgba(255, 255, 255, 0.12);
background: color-mix(in srgb, var(--surface-base) 90%, transparent);
border-color: var(--border-strong);
}
}
@ -674,14 +672,14 @@
&.default {
color: var(--claude-deep-strong);
border-color: rgba(208, 122, 20, 0.35);
background: rgba(242, 169, 59, 0.14);
border-color: color-mix(in srgb, var(--accent-deep-strong) 35%, transparent);
background: color-mix(in srgb, var(--accent-deep) 14%, transparent);
}
&.current {
color: #2f6f4e;
border-color: rgba(47, 111, 78, 0.35);
background: rgba(118, 176, 134, 0.18);
color: var(--state-success);
border-color: color-mix(in srgb, var(--state-success) 35%, transparent);
background: color-mix(in srgb, var(--state-success) 18%, transparent);
}
&.running {
@ -693,38 +691,38 @@
[data-theme='dark'] .host-workspace-header-box {
background: var(--claude-left-rail);
border-color: rgba(160, 160, 160, 0.45);
border-color: var(--border-strong);
}
[data-theme='dark'] .host-workspace-card {
background: color-mix(in srgb, var(--claude-left-rail) 88%, var(--theme-surface-strong) 12%);
&:hover:not(:disabled) {
background: #222222;
background: var(--badge-bg);
}
&.active {
background: #242424;
background: color-mix(in srgb, var(--badge-bg) 100%, white 4%);
}
}
[data-theme='dark'] .host-workspace-badge {
&.default {
color: #f7c56f;
border-color: rgba(247, 197, 111, 0.35);
background: rgba(247, 197, 111, 0.16);
color: var(--accent-deep);
border-color: color-mix(in srgb, var(--accent-deep) 35%, transparent);
background: color-mix(in srgb, var(--accent-deep) 16%, transparent);
}
&.current {
color: #8edbb4;
border-color: rgba(142, 219, 180, 0.34);
background: rgba(16, 185, 129, 0.2);
color: var(--state-success);
border-color: color-mix(in srgb, var(--state-success) 34%, transparent);
background: color-mix(in srgb, var(--state-success) 20%, transparent);
}
&.running {
color: #f7c56f;
border-color: rgba(247, 197, 111, 0.35);
background: rgba(247, 197, 111, 0.16);
color: var(--accent-deep);
border-color: color-mix(in srgb, var(--accent-deep) 35%, transparent);
background: color-mix(in srgb, var(--accent-deep) 16%, transparent);
}
}
@ -744,7 +742,7 @@
align-items: center;
justify-content: space-between;
padding: 10px 14px;
border: 1px solid rgba(118, 103, 84, 0.18);
border: 1px solid var(--border-default);
border-radius: 10px;
font-size: 13px;
color: var(--claude-text);
@ -752,8 +750,8 @@
}
.todo-task.done {
background: rgba(92, 190, 125, 0.08);
border-color: rgba(92, 190, 125, 0.3);
background: color-mix(in srgb, var(--state-success) 8%, transparent);
border-color: color-mix(in srgb, var(--state-success) 30%, transparent);
}
.todo-task-title {
@ -853,14 +851,13 @@
.context-menu {
position: fixed;
background: #ffffff;
border: 1px solid rgba(15, 23, 42, 0.08);
background: var(--surface-raised);
border: 1px solid var(--border-default);
border-radius: 8px;
box-shadow: none;
z-index: 3000;
min-width: 180px;
padding: 6px 0;
backdrop-filter: blur(8px);
}
.context-menu button {
@ -870,17 +867,17 @@
border: none;
text-align: left;
font-size: 13px;
color: #1f2933;
color: var(--text-primary);
cursor: pointer;
transition: background 0.15s ease;
}
.context-menu button:hover {
background: rgba(59, 130, 246, 0.12);
background: color-mix(in srgb, var(--state-info) 12%, transparent);
}
.context-menu button:disabled {
color: #9ca3af;
color: var(--text-tertiary);
cursor: not-allowed;
background: transparent;
}
@ -901,7 +898,7 @@
}
.sidebar-manage-btn:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
border-color: transparent;
}
@ -922,7 +919,7 @@
}
.sidebar-view-toggle:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
.panel-menu {
@ -957,5 +954,5 @@
.panel-menu button:hover,
.panel-menu button.active {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}

View File

@ -21,7 +21,7 @@
background: var(--claude-panel);
border: 1px solid var(--claude-border);
border-radius: 26px;
box-shadow: 0 24px 42px rgba(61, 57, 41, 0.18);
box-shadow: 0 24px 42px var(--shadow-color);
transition: transform 0.35s ease, opacity 0.35s ease;
pointer-events: auto;
position: relative;
@ -170,18 +170,18 @@
left: 12px;
width: 14px;
height: 14px;
border: 1px solid #e0443e;
border: 1px solid var(--mac-close-border);
border-radius: 50%;
background: #ff5f56;
background: var(--mac-close);
display: block;
padding: 0;
cursor: pointer;
transition: background 0.2s ease, transform 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.16);
box-shadow: 0 2px 4px var(--shadow-color);
}
.token-close-btn:hover {
background: #ff3b30;
background: var(--mac-close-hover);
transform: translateY(-1px);
}
@ -328,7 +328,7 @@
border-radius: 999px;
font-size: 11px;
font-weight: 600;
background: rgba(125, 109, 94, 0.15);
background: var(--badge-bg);
color: var(--claude-text-secondary);
}
@ -340,7 +340,7 @@
border-radius: 12px;
background: var(--claude-bg);
border: 1px solid var(--claude-border-strong);
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18);
box-shadow: 0 12px 30px var(--shadow-color);
z-index: 2000;
max-width: 320px;
color: var(--claude-text);
@ -388,8 +388,8 @@
padding: 12px 14px;
border-radius: 14px;
background: var(--claude-bg);
border: 1px solid var(--claude-border-strong, rgba(118,103,84,0.35));
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18);
border: 1px solid var(--claude-border-strong);
box-shadow: 0 12px 30px var(--shadow-color);
color: var(--claude-text);
}
@ -410,7 +410,7 @@
}
.app-toast--error::before {
background: #d45f5f;
background: var(--state-danger);
}
.app-toast-body {
@ -444,17 +444,17 @@
.status-pill--running {
background: rgba(118, 176, 134, 0.18);
background: color-mix(in srgb, var(--state-success) 22%, transparent);
color: var(--claude-success);
}
.status-pill--stopped {
background: rgba(217, 152, 69, 0.2);
background: color-mix(in srgb, var(--state-warning) 22%, transparent);
color: var(--claude-warning);
}
.status-pill--host {
background: rgba(125, 109, 94, 0.12);
background: var(--badge-bg);
color: var(--claude-text-secondary);
}
@ -584,9 +584,9 @@
.search-result-item {
padding: 10px 12px;
border: 1px solid rgba(118, 103, 84, 0.16);
border: 1px solid var(--border-default);
border-radius: 8px;
background: rgba(255, 255, 255, 0.65);
background: var(--surface-soft);
word-break: break-word;
}

View File

@ -45,7 +45,7 @@
height: 32px;
border: none;
border-radius: 16px;
background: rgba(0, 0, 0, 0.08);
background: var(--hover-bg);
color: var(--claude-text);
font-size: 20px;
cursor: pointer;
@ -56,7 +56,7 @@
height: 32px;
border: none;
border-radius: 16px;
background: rgba(0, 0, 0, 0.08);
background: var(--hover-bg);
color: var(--claude-text);
font-size: 20px;
cursor: pointer;
@ -66,7 +66,7 @@
flex: 0 0 auto;
min-width: 340px;
max-width: min(600px, 52vw);
height: var(--app-viewport, 100vh);
height: var(--app-viewport);
display: flex;
flex-direction: column;
background: var(--claude-left-rail);
@ -139,9 +139,9 @@
}
.git-changes-panel__close:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
color: var(--claude-text);
box-shadow: 0 1px 2px rgba(61, 57, 41, 0.08);
box-shadow: 0 1px 2px var(--shadow-color);
}
.git-changes-panel__body {
@ -149,14 +149,14 @@
min-height: 0;
overflow: auto;
padding: 12px;
scrollbar-color: color-mix(in srgb, var(--claude-text-secondary, #6b7280) 55%, transparent)
color-mix(in srgb, var(--theme-surface-muted, #d1d5db) 70%, transparent);
scrollbar-color: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent)
color-mix(in srgb, var(--theme-surface-muted) 70%, transparent);
scrollbar-width: thin;
}
.git-changes-panel * {
scrollbar-color: color-mix(in srgb, var(--claude-text-secondary, #6b7280) 55%, transparent)
color-mix(in srgb, var(--theme-surface-muted, #d1d5db) 70%, transparent);
scrollbar-color: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent)
color-mix(in srgb, var(--theme-surface-muted) 70%, transparent);
scrollbar-width: thin;
}
@ -168,14 +168,14 @@
.git-changes-panel::-webkit-scrollbar-track,
.git-changes-panel ::-webkit-scrollbar-track {
background: color-mix(in srgb, var(--theme-surface-muted, #d1d5db) 70%, transparent);
background: color-mix(in srgb, var(--theme-surface-muted) 70%, transparent);
border-radius: 999px;
margin: 8px;
}
.git-changes-panel::-webkit-scrollbar-thumb,
.git-changes-panel ::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--claude-text-secondary, #6b7280) 55%, transparent);
background: color-mix(in srgb, var(--claude-text-secondary) 55%, transparent);
border-radius: 999px;
border: 2px solid transparent;
background-clip: padding-box;
@ -189,7 +189,7 @@
}
.git-changes-panel__empty--error {
color: var(--theme-danger, #c00);
color: var(--state-danger);
}
.git-change-file {
@ -238,9 +238,9 @@
.git-change-file__open-btn:hover,
.git-change-file__open-btn.open {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
color: var(--claude-text);
box-shadow: 0 1px 2px rgba(61, 57, 41, 0.08);
box-shadow: 0 1px 2px var(--shadow-color);
}
.git-change-file__open-btn:disabled {
@ -322,8 +322,8 @@ body[data-theme='dark'] .git-change-file__open-icon {
}
.git-change-file__open-option:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
box-shadow: 0 1px 2px rgba(61, 57, 41, 0.08);
background: var(--theme-tab-active);
box-shadow: 0 1px 2px var(--shadow-color);
}
.git-change-file__open-empty {
@ -354,9 +354,9 @@ body[data-theme='dark'] .git-change-file__open-icon {
}
.git-change-file__reset:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
color: var(--claude-text);
box-shadow: 0 1px 2px rgba(61, 57, 41, 0.08);
box-shadow: 0 1px 2px var(--shadow-color);
}
.git-change-file__diff {
@ -402,9 +402,9 @@ body[data-theme='dark'] .git-change-file__open-icon {
}
.git-change-file__fold:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
color: var(--claude-text);
box-shadow: 0 1px 2px rgba(61, 57, 41, 0.08);
box-shadow: 0 1px 2px var(--shadow-color);
}
.git-change-file__fold-icon {
@ -471,13 +471,13 @@ body[data-theme='dark'] .git-change-file__open-icon {
}
.git-change-line--add {
background: rgba(0, 255, 0, 0.1);
color: #0a0;
background: var(--diff-add-bg);
color: var(--state-success);
}
.git-change-line--delete {
background: rgba(255, 0, 0, 0.1);
color: #c00;
background: var(--diff-del-bg);
color: var(--state-danger);
}
.git-changes-panel-slide-enter-active,
@ -514,8 +514,8 @@ body[data-theme='dark'] .git-change-file__open-icon {
border: 1px solid var(--claude-border);
border-radius: 12px;
overflow: hidden;
background: rgba(255, 255, 255, 0.75);
box-shadow: 0 12px 28px rgba(61, 57, 41, 0.08);
background: var(--surface-soft);
box-shadow: 0 12px 28px var(--shadow-color);
}
.tab-header {
@ -541,7 +541,7 @@ body[data-theme='dark'] .git-change-file__open-icon {
.file-content {
max-height: 320px;
overflow-y: auto;
background: #ffffff;
background: var(--surface-raised);
}
.file-content pre {
@ -553,7 +553,7 @@ body[data-theme='dark'] .git-change-file__open-icon {
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 13px;
line-height: 1.5;
color: #1f2227;
color: var(--text-primary);
}
.tool-approval-panel .approval-panel-body {
@ -621,10 +621,10 @@ body[data-theme='dark'] .git-change-file__open-icon {
line-height: 1.5;
max-height: 400px;
overflow-y: auto;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid var(--border-default);
border-radius: 6px;
padding: 8px;
background: rgba(0, 0, 0, 0.01);
background: color-mix(in srgb, transparent 99%, var(--text-primary));
scrollbar-width: none;
-ms-overflow-style: none;
}
@ -685,13 +685,13 @@ body[data-theme='dark'] .git-change-file__open-icon {
}
.tool-approval-panel .diff-remove {
background: rgba(255, 0, 0, 0.1);
color: #c00;
background: var(--diff-del-bg);
color: var(--state-danger);
}
.tool-approval-panel .diff-add {
background: rgba(0, 255, 0, 0.1);
color: #0a0;
background: var(--diff-add-bg);
color: var(--state-success);
}
.approval-actions {
@ -719,8 +719,8 @@ body[data-theme='dark'] .git-change-file__open-icon {
}
.approval-btn--switch {
color: #fff;
background: #dc2626;
color: var(--on-accent);
background: var(--state-danger);
}
.approval-btn--reject {

View File

@ -9,8 +9,8 @@
top: 0;
z-index: 120;
width: var(--conversation-expanded-width);
height: var(--app-viewport, 100vh) !important;
min-height: var(--app-viewport, 100vh) !important;
height: var(--app-viewport) !important;
min-height: var(--app-viewport) !important;
overflow: hidden;
background: var(--claude-left-rail);
border-right: 0;
@ -97,13 +97,13 @@
.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, rgba(0, 0, 0, 0.055));
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, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
outline: none;
}
@ -113,7 +113,7 @@
.conversation-sidebar:not(.collapsed) .workspace-control-btn.active:hover,
.conversation-sidebar:not(.collapsed) .workspace-control-btn.active:focus-visible {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
.sidebar-nav-label,
@ -178,7 +178,7 @@
.search-input-wrap:hover,
.search-input-wrap:focus-within {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
.search-inline-icon {
@ -257,7 +257,7 @@
.conversation-sidebar .running-task-item:hover,
.conversation-sidebar .running-task-item.active {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
.conversation-sidebar .running-task-workspace {
@ -294,7 +294,7 @@
[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: #222222;
background: var(--hover-bg);
}
.conversation-sidebar .conversation-item {
@ -360,7 +360,7 @@ body[data-theme='dark'] .conversation-sidebar .running-task-item.active {
}
.conversation-sidebar .conversation-item.duplicate-source-mask:hover::after {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
:root[data-theme='dark']
@ -369,7 +369,7 @@ body[data-theme='dark'] .conversation-sidebar .running-task-item.active {
body[data-theme='dark']
.conversation-sidebar
.conversation-item.duplicate-source-mask:hover::after {
background: rgba(255, 255, 255, 0.078);
background: var(--hover-bg);
}
.conversation-sidebar .conversation-item.duplicate-source-mask > * {
@ -407,18 +407,18 @@ body[data-theme='dark']
.conversation-sidebar .conversation-item:focus-within,
.conversation-sidebar .conversation-item.active:hover,
.conversation-sidebar .conversation-item.active:focus-within {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055)) !important;
background: var(--theme-tab-active) !important;
box-shadow: none !important;
}
.conversation-sidebar .conversation-item.active {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055)) !important;
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: rgba(255, 255, 255, 0.078) !important;
background: var(--hover-bg) !important;
box-shadow: none !important;
}
@ -430,7 +430,7 @@ 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: rgba(255, 255, 255, 0.078) !important;
background: var(--hover-bg) !important;
box-shadow: none !important;
}
@ -543,7 +543,7 @@ body[data-theme='dark'] .conversation-sidebar .conversation-item.active:focus-wi
padding: 6px;
border: 1px solid var(--claude-border);
border-radius: 14px;
background: var(--theme-surface-strong, #fff);
background: var(--theme-surface-strong);
box-shadow: none;
opacity: 0;
transform: translateY(-4px) scale(0.98);
@ -579,11 +579,11 @@ body[data-theme='dark'] .conversation-sidebar .conversation-item.active:focus-wi
}
.conversation-sidebar .conversation-actions-menu button:hover {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
}
.conversation-sidebar .conversation-actions-menu button.danger {
color: #d6483b;
color: var(--state-danger);
}
.conversation-personal-entry {
@ -626,7 +626,7 @@ body[data-theme='dark'] .conversation-sidebar .conversation-item.active:focus-wi
}
.load-more-btn:hover:not(:disabled) {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
background: var(--theme-tab-active);
color: var(--claude-text);
}
@ -716,19 +716,19 @@ body[data-theme='dark'] .conversation-sidebar .conversation-item.active:focus-wi
.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, rgba(0, 0, 0, 0.055)) !important;
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, rgba(0, 0, 0, 0.055)) !important;
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: rgba(255, 255, 255, 0.078) !important;
background: var(--hover-bg) !important;
}
.conversation-sidebar .load-more-btn {
@ -739,10 +739,10 @@ body[data-theme='dark'] .conversation-sidebar:not(.collapsed) .personal-page-btn
}
.conversation-sidebar .load-more-btn:hover:not(:disabled) {
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055)) !important;
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: rgba(255, 255, 255, 0.078) !important;
background: var(--hover-bg) !important;
}

View File

@ -3,13 +3,13 @@
position: relative;
display: flex;
flex-direction: row;
height: var(--app-viewport, 100vh);
height: var(--app-viewport);
align-items: stretch;
background: var(--claude-bg);
color: var(--claude-text);
}
#app {
min-height: var(--app-viewport, 100vh);
min-height: var(--app-viewport);
background: var(--claude-bg);
}

View File

@ -16,7 +16,7 @@
top: 0;
height: 100%;
z-index: 1000;
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
box-shadow: 2px 0 10px var(--shadow-color);
transform: translateX(-100%);
}