模型菜单视觉重做:照 Codex 观感 + 我们的暖色 token(复核反馈 2 之二)
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m57s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m6s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m51s
Project CI / Backend tests (pull_request) Successful in 8m11s
Project CI / Frontend tests (pull_request) Successful in 3m35s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m32s
Project CI / Native shell tests (pull_request) Successful in 8m45s
Project CI / Repository checks (pull_request) Successful in 7m3s

- 卡片:圆角 12 → 16px、内边距 12px、描边换成极淡 `--platform-line-soft`、投影改暖色 `rgb(112 57 30 / …)`(不用纯黑);去掉横贯分隔线,强度区与列表之间只用留白分开
- 强度区:标题 `--platform-text-strong` 12/600、右侧「当前:X」`--platform-text-soft` 11px;单列铺满(`minmax(0,1fr)`,否则 `auto` 轨道只按 max-content 定尺、滑块被压回 129px);滑块与档位点包一层相对定位容器,点改绝对定位叠在轨道中线
- 滑块:轨道 6px 圆头,已选段 `--platform-accent`、未选段 `--platform-subpanel-border`(进度由组件写进来的 `--strength-progress` 驱动双色渐变,不写死颜色);圆钮 18 → 20px(`--platform-input-highlight` 底 + `--platform-accent` 2px 描边 + 柔影),hover/active/focus-visible 与 Firefox 伪元素同步
- 档位点:5px 淡点 → 7px 且落在轨道中线上(带输入区底色的 1.5px 描边提对比),`pointer-events: none` 不挡命中
- 模型列表:行高 34px、行圆角 9px、hover `--platform-nav-item-icon-fill`、选中 `--platform-warm-bg` 轻量 pill(覆盖前文那几条 `!important` 同属性规则);`max-height: 170px`(=5×34,不出现半行);滚动条 6px、平时透明,悬停卡片才显形
- 校验:typecheck、4 个 vitest 文件 212 passed / 9 skipped、eslint、prettier、encoding、spacetime-schema、diff-check 全通过;无头 Chromium 夹具(447px / 900px)截图为证(见 PR 正文)
This commit is contained in:
2026-10-05 00:34:59 +08:00
parent c197edb203
commit 3383c13bba
3 changed files with 203 additions and 71 deletions
+159 -42
View File
@@ -13471,11 +13471,11 @@ details.design-agent-reasoning[open]
队列 chip 与提示段落的 `margin` 都写回前文各自那条规则。 */
/* ④ 思维强度**并入模型菜单**:底行只留模型触发钮一颗控件,强度区嵌在菜单顶部。
菜单外壳(`.conversation-model-menu-shell`)沿用原来模型菜单的定位 / 描边 / 底色 /
阴影(向上展开、贴触发钮右缘),强度和模型列表共用同一张卡;强度区里是原生
`<input type="range">`,自带 `role="slider"`、`aria-valuemin/max/now` 与
←/→/Home/End,`aria-valuetext` 报中文档位名,点轨道或拖拽按 `step` 吸附到档位。
颜色全部走 `--platform-*`(不引入新色值)。 */
菜单外壳(`.conversation-model-menu-shell`)沿用原来模型菜单的锚点与朝向(贴触发钮
右缘、向上展开、距输入区 8px),观感按 Codex 参考图重做:大圆角、松内边距、柔和阴影 +
极淡描边,颜色全部取 `--platform-*` 暖色 token(不引入新色值)。
强度区里是原生 `<input type="range">`,自带 `role="slider"`、`aria-valuemin/max/now`
与 ←/→/Home/End,`aria-valuetext` 报中文档位名,点轨道或拖拽按 `step` 吸附到档位。 */
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
@@ -13484,20 +13484,24 @@ details.design-agent-reasoning[open]
right: 0;
bottom: calc(100% + 8px);
z-index: 20;
display: grid;
display: flex;
flex-direction: column;
gap: 6px;
width: max-content;
min-width: 216px;
min-width: 260px;
max-width: min(320px, calc(100vw - 24px));
border: 1px solid var(--platform-surface-border);
border-radius: 12px;
padding: 12px;
border: 1px solid var(--platform-line-soft);
border-radius: 16px;
background: var(--platform-input-fill);
box-shadow:
0 14px 30px rgb(31 41 55 / 13%),
0 2px 6px rgb(31 41 55 / 5%);
0 18px 40px rgb(112 57 30 / 14%),
0 2px 8px rgb(112 57 30 / 6%);
pointer-events: auto;
}
/* 里层菜单回到文档流:外壳已经有位置 / 描边 / 底色,列表只负责滚动与截断。 */
/* 里层菜单回到文档流:外壳已经有位置 / 描边 / 底色,列表只负责滚动与截断。
行高更舒展(34px)、行圆角 9px、滚动条细且平时透明(悬停卡片才显形)。 */
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
@@ -13507,12 +13511,90 @@ details.design-agent-reasoning[open]
width: 100%;
min-width: 0;
max-width: none;
max-height: 260px;
/* 170 = 5 行 × 34px:不会停在半行上;再长就滚动(细滚动条,悬停显形)。 */
max-height: 170px;
overflow: auto;
padding: 0;
border: 0;
border-radius: 0;
border-radius: 12px;
background: transparent;
box-shadow: none;
scrollbar-width: thin;
scrollbar-color: var(--platform-subpanel-border) transparent;
}
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.conversation-model-menu-shell
.conversation-model-menu::-webkit-scrollbar {
width: 6px;
}
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.conversation-model-menu-shell
.conversation-model-menu::-webkit-scrollbar-track {
background: transparent;
}
/* 静止时不画滚动条,鼠标进卡片才显形("细 + 自动隐藏")。 */
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.conversation-model-menu-shell
.conversation-model-menu::-webkit-scrollbar-thumb {
border-radius: 999px;
background: transparent;
}
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.conversation-model-menu-shell:hover
.conversation-model-menu::-webkit-scrollbar-thumb {
background: var(--platform-subpanel-border);
}
/* 行:34px 高、9px 圆角、hover 极淡暖底,选中是轻量 pill(不做重色块)。
前文那几条 `!important` 的同属性规则在这里按同一档 `!important` + 更高特异性盖回。 */
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.conversation-model-menu-shell
.conversation-model-menu
button {
min-height: 34px !important;
padding: 0 10px !important;
border-radius: 9px !important;
}
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.conversation-model-menu-shell
.conversation-model-menu
button:hover:not(:disabled),
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.conversation-model-menu-shell
.conversation-model-menu
button:focus-visible {
background: var(--platform-nav-item-icon-fill) !important;
color: var(--platform-text-strong) !important;
}
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.conversation-model-menu-shell
.conversation-model-menu
button[aria-selected='true'] {
background: var(--platform-warm-bg) !important;
color: var(--platform-text-strong) !important;
font-weight: 600 !important;
}
/* 列表宽度由外壳限定,超长模型名走省略号(原来靠 `width: max-content` 不截断)。 */
@@ -13536,16 +13618,30 @@ details.design-agent-reasoning[open]
white-space: nowrap;
}
/* 强度区:菜单顶部、与模型列表用一条分隔线分开(分隔线是最后一个子属性的
`border-bottom`,列表首项自带 padding,不会显得挤)。 */
/* 强度区:菜单顶部,与列表之间只用留白分开(不画横贯分隔线),标题左右层级分明。 */
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.project-chat-reasoning-effort.is-section {
display: grid;
gap: 6px;
padding: 9px 9px 7px;
border-bottom: 1px solid var(--platform-line-soft);
/* 单列铺满:`auto` 轨道在这里只按 max-content 定尺,滑块会被压回固有宽度。 */
grid-template-columns: minmax(0, 1fr);
justify-items: stretch;
width: 100%;
align-self: stretch;
gap: 10px;
padding: 4px 6px 2px;
}
/* 滑块与档位点是一个整体:点绝对定位叠在轨道中线上(不受字号 / 行高变化影响)。 */
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-slider {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr);
width: 100%;
}
.game-workbench-chat
@@ -13553,7 +13649,7 @@ details.design-agent-reasoning[open]
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-head {
display: flex;
align-items: center;
align-items: baseline;
justify-content: space-between;
gap: 8px;
min-width: 0;
@@ -13563,9 +13659,9 @@ details.design-agent-reasoning[open]
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-title {
color: var(--platform-text-base);
color: var(--platform-text-strong);
font-size: 12px;
font-weight: 700;
font-weight: 600;
}
.game-workbench-chat
@@ -13573,7 +13669,7 @@ details.design-agent-reasoning[open]
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-range {
width: 100%;
height: 24px;
height: 26px;
margin: 0;
appearance: none;
border: 0;
@@ -13587,21 +13683,26 @@ details.design-agent-reasoning[open]
.project-chat-composer-strength-range::-webkit-slider-runnable-track {
height: 6px;
border-radius: 999px;
background: var(--platform-button-ghost-fill);
/* 已选段用主色、未选段用暖灰:进度由组件写进来的 `--strength-progress` 决定。 */
background: linear-gradient(
to right,
var(--platform-accent) 0 var(--strength-progress, 0%),
var(--platform-subpanel-border) var(--strength-progress, 0%) 100%
);
}
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-range::-webkit-slider-thumb {
width: 18px;
height: 18px;
margin-top: -6px;
width: 20px;
height: 20px;
margin-top: -7px;
appearance: none;
border: 2px solid var(--platform-button-primary-fill);
border: 2px solid var(--platform-accent);
border-radius: 999px;
background: var(--platform-input-fill);
box-shadow: 0 1px 2px rgb(0 0 0 / 18%);
background: var(--platform-input-highlight);
box-shadow: 0 2px 6px rgb(112 57 30 / 22%);
}
.game-workbench-chat
@@ -13651,19 +13752,28 @@ details.design-agent-reasoning[open]
.project-chat-composer-strength-range::-moz-range-track {
height: 6px;
border-radius: 999px;
background: var(--platform-button-ghost-fill);
background: var(--platform-subpanel-border);
}
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-range::-moz-range-progress {
height: 6px;
border-radius: 999px;
background: var(--platform-accent);
}
.game-workbench-chat
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-range::-moz-range-thumb {
width: 18px;
height: 18px;
border: 2px solid var(--platform-button-primary-fill);
width: 20px;
height: 20px;
border: 2px solid var(--platform-accent);
border-radius: 999px;
background: var(--platform-input-fill);
box-shadow: 0 1px 2px rgb(0 0 0 / 18%);
background: var(--platform-input-highlight);
box-shadow: 0 2px 6px rgb(112 57 30 / 22%);
}
/* 档位点:纯装饰(命中与吸附由原生 range 负责),个数跟着档位契约走,
@@ -13672,11 +13782,17 @@ details.design-agent-reasoning[open]
.project-chat-surface.is-direct-codex
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-stops {
position: absolute;
top: 50%;
right: 0;
left: 0;
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
align-items: center;
justify-items: center;
margin-top: -12px;
height: 6px;
transform: translateY(-50%);
pointer-events: none;
}
@@ -13685,10 +13801,11 @@ details.design-agent-reasoning[open]
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-stops
span {
width: 5px;
height: 5px;
width: 7px;
height: 7px;
border: 1.5px solid var(--platform-input-highlight);
border-radius: 999px;
background: var(--platform-surface-border);
background: var(--platform-text-muted);
}
.game-workbench-chat
@@ -13696,7 +13813,7 @@ details.design-agent-reasoning[open]
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-stops
span.is-active {
background: var(--platform-button-primary-fill);
background: var(--platform-accent);
}
.game-workbench-chat
@@ -13704,6 +13821,6 @@ details.design-agent-reasoning[open]
.project-chat-composer.is-direct-codex
.project-chat-composer-strength-value {
color: var(--platform-text-soft);
font-size: 12px;
font-size: 11px;
text-align: right;
}
@@ -17,7 +17,7 @@ import {
Square,
X,
} from 'lucide-react';
import type { RefObject } from 'react';
import type { CSSProperties, RefObject } from 'react';
import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
@@ -476,35 +476,47 @@ export function ComposerReasoningEffortSelect({
{`当前:${activeLabel}`}
</span>
</span>
{/* 原生 range 的 role/值域本来就成立,这里再显式写一遍:`aria-valuetext` 是
中文档位名,读屏念的是「最高」而不是「4」。 */}
<input
type="range"
className="project-chat-composer-strength-range"
min={0}
max={options.length - 1}
step={1}
value={activeIndex}
aria-label="思维强度"
aria-valuemin={0}
aria-valuemax={options.length - 1}
aria-valuenow={activeIndex}
aria-valuetext={activeLabel}
disabled={disabled || saving}
onChange={(event) => {
const next = options[Number(event.currentTarget.value)];
if (next && next.value !== effort) {
selectEffort(next.value);
<span className="project-chat-composer-strength-slider">
{/* 原生 range 的 role/值域本来就成立,这里再显式写一遍:`aria-valuetext` 是
中文档位名,读屏念的是「最高」而不是「4」。 */}
<input
type="range"
className="project-chat-composer-strength-range"
min={0}
max={options.length - 1}
step={1}
value={activeIndex}
style={
{
/* 已选段填充的进度:CSS 里用它给轨道做双色渐变(不写死颜色)。 */
'--strength-progress': `${(activeIndex / (options.length - 1)) * 100}%`,
} as CSSProperties
}
}}
/>
<span className="project-chat-composer-strength-stops" aria-hidden="true">
{options.map((option, index) => (
<span
key={option.value}
className={index === activeIndex ? 'is-active' : undefined}
/>
))}
aria-label="思维强度"
aria-valuemin={0}
aria-valuemax={options.length - 1}
aria-valuenow={activeIndex}
aria-valuetext={activeLabel}
disabled={disabled || saving}
onChange={(event) => {
const next = options[Number(event.currentTarget.value)];
if (next && next.value !== effort) {
selectEffort(next.value);
}
}}
/>
{/* 档位点叠在轨道上(纯装饰,命中与吸附由原生 range 负责)。 */}
<span
className="project-chat-composer-strength-stops"
aria-hidden="true"
>
{options.map((option, index) => (
<span
key={option.value}
className={index === activeIndex ? 'is-active' : undefined}
/>
))}
</span>
</span>
</span>
);
@@ -18,6 +18,9 @@
- 关掉旧的独立触发钮(底行元素 2 → 1);浮层的开合、Esc 与点击外部关闭都由模型菜单既有行为负责,强度区不再自带浮层。
- **取舍**:强度区随菜单挂载,打开时先显示「默认」再回读成落盘档位(一次 IPC 往返),测试里要 `waitFor`;换掉这一点需要把档位状态提到宿主,暂不做。
- **判据/取证**:`tests/appSurface/chat-composer.suite.ts`(底行不再有第二颗控件、菜单里改档落同一条持久化通道、Esc/点外部关闭、滑块不在 listbox 内)+ `tests/chatDialogFrameLayout.test.ts`(外壳向上展开 / 强度区网格 / 底行高度不增)+ `tests/appSurface/design-agent.suite.ts`(策划菜单里没有滑块)。
- **视觉口径(用户复核二次要求)**:卡片与滑块的观感照 Codex 参考图,颜色只取我们自己的暖色 token——卡片圆角 16px、内边距 12px、极淡描边(`--platform-line-soft`)+ 柔和暖色投影(`rgb(112 57 30 / …)`,不用纯黑)、**不画横贯分隔线**(只靠留白);强度区标题 `--platform-text-strong` 12/600、右侧当前档位 `--platform-text-soft` 11px;轨道 6px 圆头,已选段 `--platform-accent`、未选段 `--platform-subpanel-border`(进度由组件写进来的 `--strength-progress` 驱动渐变),圆钮 20px(`--platform-input-highlight` 底 + `--platform-accent` 2px 描边 + 柔影),档位点 7px 绝对定位叠在轨道中线(`pointer-events: none`,不挡命中);模型列表行高 34px、行圆角 9px、hover `--platform-nav-item-icon-fill`、选中 `--platform-warm-bg` 轻量 pill + 右侧对勾;列表 `max-height: 170px`(=5×34,不出现半行),滚动条 6px 且平时透明、悬停卡片才显形。
- **实测(无头 Chromium 夹具,447px / 900px)**:卡片 260×263(圆角 16 / 内边距 12)、强度区 234×61、滑块 222×26、档位点 7px 且中心与轨道中线重合(点 270 / 轨道 269)、列表行高 34、`max-height` 170(内容 315 → 滚动)、默认档进度 0%,按 `End` 到 100% 且文案变「当前:最高」;卡片右缘未溢出视口。
- **关联**:`features/project-workspace/ConversationModelSelect.tsx`、`view/project-development/chat/components/DirectProjectComposer/{DirectProjectComposer.tsx,ComposerControls.tsx}`、`src/styles.css`。
## 2026-10-03 AGC 输入区控件叠在一起:输入区内的操作排与状态提示不能用绝对定位