From 58f6eceb69c45ce4f4b57925db154af29d85771d Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Tue, 15 Sep 2026 10:37:28 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=BE=93=E5=85=A5=E7=9B=92?= =?UTF-8?q?=E5=9B=9B=E8=BE=B9=E7=95=99=E7=99=BD=E4=B8=8E=E6=96=87=E5=AD=97?= =?UTF-8?q?/=E6=8E=A7=E4=BB=B6=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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;高度算式同步 --- apps/ai-game-creator-shell/src/styles.css | 135 ++++++++++++++---- .../appSurface/project-development.suite.ts | 6 +- .../tests/chatDialogFrameLayout.test.ts | 48 +++++-- 3 files changed, 142 insertions(+), 47 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index 9645a4c9a..c56165636 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -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; +} diff --git a/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts index 64815a138..5f9601540 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts @@ -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);', ); diff --git a/apps/ai-game-creator-shell/tests/chatDialogFrameLayout.test.ts b/apps/ai-game-creator-shell/tests/chatDialogFrameLayout.test.ts index e9f8fb36c..d9c8d1135 100644 --- a/apps/ai-game-creator-shell/tests/chatDialogFrameLayout.test.ts +++ b/apps/ai-game-creator-shell/tests/chatDialogFrameLayout.test.ts @@ -122,6 +122,7 @@ function marginBox(declarations: Map) { ============================================================ */ 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');