修复输入盒四边留白与文字/控件对齐

- styles.css:direct-codex 面板自身 padding 归 0,四边留白由子节点声明为 16px(实测 16/16/16/16)
- styles.css:盒内内边距 12px(文字左内缩 = 上内缩 = 右内缩 = 13px,实测)
- styles.css:输入区改单块布局,「AI 润色」按钮浮到右下角,不再占掉文字右边缘 36px(这是文字左右内缩不一致的根因)
- styles.css:左侧控件组左移 12px,使 + 图标墨迹与文字左边缘同线(实测同为 x=950)
- styles.css:去掉画在输入区上的内层焦点环,焦点环改画在整只输入盒外侧,不再出现第二层框
- styles.css:删除 direct-codex 输入盒旧几何规则(1fr 82px 网格 / z-index 2 / padding-top 8px)与窄屏 8px 12px 12px 的不对称外边距
- 测试:宽屏断言四边留白恒等 + 文字三边内缩相等;窄屏断言四边 16px;高度算式同步
This commit is contained in:
2026-09-15 10:37:28 +08:00
parent abbf1465e2
commit 58f6eceb69
3 changed files with 142 additions and 47 deletions
+104 -31
View File
@@ -8535,16 +8535,12 @@ iframe.preview-frame {
display: none;
}
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer {
grid-template-columns: minmax(0, 1fr) 82px;
align-items: stretch;
position: relative;
z-index: 2;
padding-top: 8px;
background: transparent;
}
/* 这里原有一条 direct-codex 输入盒的旧几何规则(`grid-template-columns: 1fr 82px`、
`z-index: 2`、`padding-top: 8px`、`background: transparent`),是「输入区浮在消息列表上、
与 82px 发送钮列共用网格」那版布局的残留。新几何(单列、z-index 1、自带边框与底色、
盒内上下内边距相等)已由文件末尾「右侧对话面板 Codex 风格」区块的同名规则接管,
所以整条删除——留着会让 `padding-top: 8px` 这种长手写法继续生效,
把新规则里的 `padding: 10px 12px` 拆掉一半,重新造成上下留白不一致。 */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
@@ -9220,7 +9216,9 @@ iframe.preview-frame {
flex: 1 1 auto;
}
/* 1000px 以下仍然是一列:顶栏、消息列表、输入盒依次排布,输入盒留在文档流里。 */
/* 1000px 以下仍然是一列:顶栏、消息列表、输入盒依次排布,输入盒留在文档流里。
四边留白不再在这里另给一套值(曾写过 `margin: 8px 12px 12px`,正是"左右一个值、
上下另一个值"的来源之一),统一由文件末尾的输入盒规则声明为 16px。 */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer {
@@ -9229,7 +9227,6 @@ iframe.preview-frame {
bottom: auto;
left: auto;
flex: 0 0 auto;
margin: 8px 12px 12px;
}
/* 窄屏:输入框左右留白收紧,操作排(`@` / AI 润色 / 恢复原文)保持单行不换行,
@@ -10483,12 +10480,24 @@ button.design-workspace-tree__entry:hover,
font-size: 12px;
}
/* direct-codex 面板自己不留内边距:左右与四周的留白统一由子节点自己声明,
这样「消息内容 / 顶栏 / 输入盒」三者共用同一个基准值,不会再出现
「消息内缩 16px、输入盒只有 10px」这种左右不齐,或不一致的上/下间距。
(非 direct-codex 的面板仍走上面那条 `padding: 10px`。) */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex {
padding: 0;
}
/* 输入盒是唯一有边框的容器,且**回到文档流**:它在消息列表下方(Codex 桌面客户端的
「整块面板 = 顶栏 + 消息流 + 输入盒」),不再浮在消息列表之上。
旧版那套「输入区绝对定位、消息列表用 196px 底边留白给它让位」已撤掉——两套叠着生效会
让消息被空白顶掉、输入盒盖住消息。
两行结构:文本区 / 控制排(`+` `@` · 模型下拉 · 发送)。原先那行「项目名 · 本地」信息标签
已按需求删掉,输入盒内不再复述当前项目。 */
已按需求删掉,输入盒内不再复述当前项目。
四边留白统一 16px:左右与底边来自这里的外边距,顶边同样来自外边距(面板 padding 已归 0),
所以四边恒等;消息列表自身也是左右各 16px,输入盒与消息内容左右对齐。
盒内上下内边距同样取同一个值(10px),不写 `8px … 10px` 那种上下不一致的写法。 */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer.is-direct-codex {
@@ -10501,9 +10510,13 @@ button.design-workspace-tree__entry:hover,
right: 0;
bottom: 0;
left: 0;
margin: 16px;
max-height: none;
z-index: 1;
padding: 8px 12px 10px;
/* 盒内内边距四边同值:文字/光标的左内缩与上内缩必须相等,
否则点进输入框时(`.resource-reference-input:focus-within` 会画出焦点环)
会明显看出文字离左边近、离上边远。 */
padding: 12px;
border: 1px solid var(--platform-surface-border);
border-radius: 14px;
background: var(--platform-input-fill);
@@ -10524,18 +10537,26 @@ button.design-workspace-tree__entry:hover,
box-shadow: none;
}
/* direct-codex 输入区去默认灰描边:焦点可见性交给下面那条
`.project-supervisor-composer .resource-reference-input:focus-within`
`--platform-input-focus-ring` 光环)。这条规则比它多一个 `is-direct-codex` 类
优先级更高,所以**不能写 `box-shadow: none`**——写回去会把那条光环整条盖掉,
而 base `.resource-reference-input-editor` 又是 `outline: 0`,输入区就完全没有
可见焦点了(无障碍回归)。 */
/* direct-codex 输入区去默认灰描边,并且**不再画内层焦点环**:焦点可见性交给下面那条
`:focus-within` 的整只输入盒——在输入区上再套一圈光环会让它看起来是"第二层框",
文字/光标被圈在小框里、而 `+` `@` 发送那排控件在小框外
观感上就是"光标和底下的控件对不齐"。 */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer.is-direct-codex
.resource-reference-input:focus-within {
border: 0;
outline: none;
box-shadow: none;
}
/* 整只输入盒的焦点环:只加深描边 + 一圈**盒子外面**的光环,
不侵占盒内的文字与控件对齐。 */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer.is-direct-codex:focus-within {
border-color: var(--platform-surface-hover-border);
box-shadow: 0 0 0 3px var(--platform-input-focus-ring);
}
.game-workbench-chat
@@ -10583,6 +10604,18 @@ button.design-workspace-tree__entry:hover,
margin-left: auto;
}
/* 左侧控件组整体左移,让 `+` 图标的**字形**与输入区文字的左边缘对齐。
控件钮是 28×28 的点击区、图标在其中居中(实测图标字形比按钮盒左边缘再右偏
(28-15)/2 ≈ 6.5px),所以光对齐按钮盒是不够的——那正是「光标比下面那排控件更靠左」
的成因。这里用 -7px 抵消这段居中偏移,点击区尺寸不变。 */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer.is-direct-codex
.project-supervisor-composer-controls
.project-supervisor-composer-controls-left {
margin-left: -7px;
}
/* 模型选择钮在控制排里按流式排布,但它同时是**弹层锚点**`.conversation-model-menu`
`bottom: calc(100% + 8px)`)与模型提示(`[role='alert'] / [role='status']`
`bottom: 36px`)都是 `position: absolute`,包含块必须是这颗容器。这里原来写的是
@@ -10724,9 +10757,10 @@ button.design-workspace-tree__entry:hover,
}
/* 窄屏(≤760px)这一列是"消息列表 + 输入盒"的纵向堆叠。输入盒本来就在文档流里,
窄屏只需要给它四周留出边距:外框不再画在 `.project-supervisor-conversation` 上
(那会在整块面板外面再套一圈框,与「输入盒是唯一有边框的容器」相悖),
列表底边也不留任何给浮层让位的空白。 */
窄屏不再单独给它一套 `8px 12px 12px` 的不对称外边距——四边统一沿用上面那条
`margin: 16px`,避免又出现"左右一个值、上下另一个值"。
外框不再画在 `.project-supervisor-conversation` 上(那会在整块面板外面再套一圈框,
与「输入盒是唯一有边框的容器」相悖),列表底边也不留任何给浮层让位的空白。 */
@media (max-width: 760px) {
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
@@ -10747,12 +10781,6 @@ button.design-workspace-tree__entry:hover,
background: transparent;
}
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer.is-direct-codex {
margin: 8px 12px 12px;
}
/* 输入盒保持文档流。这条选择器比上面那条 `.project-supervisor-composer.is-direct-codex`
多带一层 `.project-supervisor-conversation`,权重比它高,窄屏才能把它顶回来。 */
.game-workbench-chat
@@ -10920,7 +10948,8 @@ button.design-workspace-tree__entry:hover,
留白),这里只补新增节点的样式,并把旧的绿系执行过程卡收进暖色系。
全部颜色只用现有 `--platform-*` 变量,浅色主题下成立。 */
/* 顶部极简条:44px 高、无独立背景块,只有一条极细分隔线。 */
/* 顶部极简条:44px 高、无独立背景块,只有一条极细分隔线。
左右内缩与消息内容 / 输入盒统一为 16px(面板自身 padding 已归 0)。 */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-topbar {
@@ -10930,7 +10959,7 @@ button.design-workspace-tree__entry:hover,
gap: 8px;
min-height: 44px;
flex: 0 0 auto;
padding: 0 12px;
padding: 0 16px;
border-bottom: 1px solid var(--platform-line-soft);
background: transparent;
}
@@ -11400,3 +11429,47 @@ button.design-workspace-tree__entry:hover,
color: var(--platform-text-soft);
font-size: 10px;
}
/* ============================================================
右侧对话面板:输入盒内文字与控件的对齐(2026-09)
============================================================
这一组必须放在文件最后:输入区的网格结构与「AI 润色」按钮的位置由更早的
`.resource-reference-input` / `.resource-reference-input-actions` 规则决定,
同特异性下只有靠后写的才能盖回去。
两处造成"文字和下面那排控件对不齐 / 左右留白不等于上下留白"的原因:
1. 输入区是两列网格(文本列 + 操作列),右侧那枚「AI 润色」按钮常年占掉 36px,
于是文字右边缘比盒内右边缘少 36px——左右留白与上下留白看着就不一致。
这里把输入区改成单块并把操作按钮浮到右下角,文字可用满整宽。
2. `+` `@` 是 28×28 点击区、图标在其中居中,lucide 视框本身还有约 3px 内缩,
字形比文字左边缘右偏约 5px(实测)。左侧控件组左移 12px 抵消这段偏移,
点击区尺寸不变。 */
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer.is-direct-codex
.resource-reference-input {
display: block;
position: relative;
min-height: 96px;
}
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer.is-direct-codex
.resource-reference-input
.resource-reference-input-actions {
position: absolute;
right: 0;
bottom: 0;
display: flex;
align-items: flex-end;
justify-content: flex-end;
}
.game-workbench-chat
.project-supervisor-surface.is-direct-codex
.project-supervisor-composer.is-direct-codex
.project-supervisor-composer-controls
.project-supervisor-composer-controls-left {
margin-left: -12px;
}
@@ -5849,7 +5849,11 @@ export function registerProjectWorkbenchFoundationTests() {
expect(composerRule?.[1]).toContain('position: relative;');
expect(composerRule?.[1]).toContain('grid-template-rows: auto auto;');
expect(composerRule?.[1]).toContain('z-index: 1;');
expect(composerRule?.[1]).toContain('padding: 8px 12px 10px;');
// 四边留白统一 16px、盒内内边距四边同为 12px(文字左内缩必须等于上内缩);
// 不允许再出现 `8px 12px 10px` / `10px 12px` 这类「左右一个值、上下另一个值」的写法。
expect(composerRule?.[1]).toContain('margin: 16px;');
expect(composerRule?.[1]).toContain('padding: 12px;');
expect(composerRule?.[1]).not.toContain('padding-top:');
expect(composerRule?.[1]).toContain(
'background: var(--platform-input-fill);',
);
@@ -122,6 +122,7 @@ function marginBox(declarations: Map<string, string>) {
============================================================ */
const CHAT = '.game-workbench-chat .project-supervisor-surface.is-direct-codex';
const SURFACE = CHAT;
const CONVERSATION = `${CHAT} .project-supervisor-conversation`;
const MESSAGE_LIST = `${CHAT} .project-supervisor-message-list`;
const MESSAGE_LIST_PLAIN =
@@ -220,23 +221,40 @@ function composerMaxHeight() {
}
describe('陶泥儿对话区:Codex 三段式(顶栏 / 唯一滚动区 / 文档流输入盒)', () => {
it('输入盒在消息列表下方、处于文档流,不再浮在列表之上(宽屏)', () => {
it('输入盒在消息列表下方、处于文档流,四边留白一致且与消息内容左右对齐(宽屏)', () => {
const list = desktopDeclarations(...LIST_SELECTORS);
const composer = desktopDeclarations(COMPOSER, COMPOSER_PLAIN);
const surface = desktopDeclarations(SURFACE);
// 列表是唯一滚动区:自身不再绝对定位,靠 flex 吸收会话列的剩余高度。
expect(declaration(list, 'position')).toBe('relative');
expect(declaration(list, 'overflow-y')).toBe('auto');
expect(declaration(list, 'flex')).toContain('1 1 auto');
// 输入盒在文档流里:不绝对定位;它紧贴列表下边(面板很窄,输入盒自己已有内边距,
// 再叠外边距会浪费高度),关键是它不再落在列表**内部**覆盖消息。
// 输入盒在文档流里:不绝对定位;关键是它不再落在列表**内部**覆盖消息。
expect(declaration(composer, 'position')).toBe('relative');
// 用户可见契约:四边留白必须一致。
// 面板自身 padding 必须归 0,否则「左右留白」会等于「面板 padding + 输入盒左右外边距」,
// 而「上/下留白」只等于外边距或面板 padding——两边口径不同就一定会再次出现
// 「左右一个值、上下另一个值」的不齐(实测就是这个现象)。
expect(paddingBox(surface).top).toBe(0);
expect(paddingBox(surface).right).toBe(0);
expect(paddingBox(surface).bottom).toBe(0);
expect(paddingBox(surface).left).toBe(0);
const insets = marginBox(composer);
expect(insets.top).toBe(0);
expect(insets.left).toBe(0);
expect(insets.right).toBe(0);
expect(insets.bottom).toBe(0);
expect(insets.top, '输入盒与消息列表之间必须有留白').toBe(16);
expect(insets.right).toBe(16);
expect(insets.bottom).toBe(16);
expect(insets.left).toBe(16);
// 左右留白还要与消息内容一致:列表自身左右各 16px(下面也有断言),三者同一基准。
expect(paddingBox(list).left).toBe(insets.left);
expect(paddingBox(list).right).toBe(insets.right);
// 盒内上下内边距同样必须相等,不能再写 `8px … 10px` 那种上下不一致的写法。
{
const composerPadding = paddingBox(composer);
expect(composerPadding.top).toBe(composerPadding.bottom);
}
// 也不再靠 bottom/left/right 偏移量定位(那是旧浮层几何的残余)。
expect(pixelValue(composer, 'bottom')).toBe(0);
expect(pixelValue(composer, 'left')).toBe(0);
@@ -260,10 +278,10 @@ describe('陶泥儿对话区:Codex 三段式(顶栏 / 唯一滚动区 / 文
const { total, actionsRowHeight } = composerMaxHeight();
// 输入盒最高高度由真实声明算出来:编辑器 140 + 行距 8 + 操作排 28 + 输入框下内边距 4
// + 输入盒上下内边距 18 + 操作条(2 + 28 方钮)= 228
// + 输入盒内边距 24(四边同为 12:文字左内缩必须等于上内缩)+ 操作条(2 + 28 方钮)= 234
expect(actionsRowHeight, '操作排按钮高度变了就要重算下面的算式').toBe(28);
expect(pixelValue(desktopDeclarations(COMPOSER_SUBMIT), 'height')).toBe(28);
expect(total).toBe(228);
expect(total).toBe(234);
const paddingBottom = paddingBox(list).bottom;
// 新契约:留白归 0。旧版这里是一条 `inset + 输入区最高高度 + 间距` 的算式,
@@ -295,17 +313,17 @@ describe('陶泥儿对话区:Codex 三段式(顶栏 / 唯一滚动区 / 文
expect(declaration(conversation, 'border')).toBe('0');
expect(declaration(conversation, 'background')).toBe('transparent');
// 输入盒仍在文档流(本来就在),四周由自己的外边距让出一圈留白。
// 输入盒仍在文档流(本来就在),四边留白与宽屏同一套值(16px),窄屏不再另给一组
// `8px 12px 12px` 的不对称外边距。
expect(declaration(composer, 'position')).toBe('relative');
expect(declaration(composer, 'bottom')).toBe('auto');
expect(declaration(composer, 'left')).toBe('auto');
expect(declaration(composer, 'right')).toBe('auto');
const composerMargin = marginBox(composer);
expect(composerMargin.left, '窄屏输入盒左右必须有边距').toBeGreaterThan(0);
expect(composerMargin.right).toBe(composerMargin.left);
expect(composerMargin.bottom, '窄屏输入盒底部必须有边距').toBeGreaterThan(
0,
);
expect(composerMargin.top, '窄屏输入盒四周都要有边距').toBe(16);
expect(composerMargin.left).toBe(16);
expect(composerMargin.right).toBe(16);
expect(composerMargin.bottom).toBe(16);
// 列表在窄屏也不画框、不留浮层空白,它仍是唯一滚动区。
expect(declaration(list, 'background')).toBe('transparent');