From 5b4f9e961d1e6f3924c75aac37731e40ceb8336a Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Mon, 21 Sep 2026 00:26:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=89=E7=BE=8E=E6=9C=AF=E7=94=BB=E5=B8=83?= =?UTF-8?q?=E5=8E=9F=E6=A0=B7=E8=A1=A5=E9=BD=90=E5=8A=A8=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF=E6=A0=B7=E5=BC=8F=EF=BC=9A=E5=85=B3=E9=97=AD=E9=94=AE?= =?UTF-8?q?=E8=90=BD=E4=BD=8D=E3=80=81=E6=8F=90=E7=A4=BA=E8=AF=8D=E6=80=81?= =?UTF-8?q?=E4=B8=8E=20760px=20=E6=95=B4=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 补 `.image-canvas-editor__generation-close` 的第二条同名规则(浮层内绝对定位):上一版只搬了基础外观,关闭键因此掉进网格流、独自占一行并居中 - 补提示词输入区的聚焦态与 aria-invalid 两档,以及 composer 内参数簇的透明底色与 hover / 展开态 - 新增 `@media (max-width: 760px)` 整档照抄(浮层落底部整宽、composer 定宽、动作竖排、参数与提交整宽),替换上一版按 720px 手写的那一小段,选择器与断点都与网页端逐字一致 - 守卫升级为「选择器 + 声明」逐条比对:同名规则只搬第一条不再判过;范围限定在 `image-canvas-editor__` 命名空间(共享 chrome 类与全局 reset 不归宿主 chrome 表照抄) --- .../resource-canvas/resourceCanvasChrome.css | 96 ++++++++++++++++++- ...anvasCharacterAnimationPanelStyle.test.tsx | 66 +++++++++++++ 2 files changed, 160 insertions(+), 2 deletions(-) diff --git a/apps/ai-game-creator-shell/src/features/resource-canvas/resourceCanvasChrome.css b/apps/ai-game-creator-shell/src/features/resource-canvas/resourceCanvasChrome.css index a485ea740..cfdb5bb14 100644 --- a/apps/ai-game-creator-shell/src/features/resource-canvas/resourceCanvasChrome.css +++ b/apps/ai-game-creator-shell/src/features/resource-canvas/resourceCanvasChrome.css @@ -1719,8 +1719,70 @@ button.image-canvas-editor__reference-chip:disabled { color: var(--image-canvas-brand-text-on-fill); } -/* 窄屏与上面的浮层工具条同档(src/index.css:9020 用 760px):面板落到底部整宽、动作竖排。 */ -@media (max-width: 720px) { +/* src/index.css:6811:提示词输入区的聚焦态(快速编辑 / 动画面板共用这一档)。 */ +.image-canvas-editor__generation-prompt:focus-within, +.image-canvas-editor__video-prompt:focus { + border-color: var(--image-canvas-brand-border-strong); + background: #ffffff; + box-shadow: 0 0 0 3px var(--image-canvas-brand-focus-ring); +} + +/* src/index.css:6822 */ +.image-canvas-editor__generation-prompt:has( + .auto-grow-text-area__content[aria-invalid='true'] + ) { + border-color: #e11d48; + background: #fff7f8; +} + +/* src/index.css:6829 */ +.image-canvas-editor__generation-prompt:has( + .auto-grow-text-area__content[aria-invalid='true'] + ):focus-within { + border-color: #e11d48; + box-shadow: 0 0 0 3px rgba(225, 29, 72, 0.14); +} + +/* + * src/index.css:7630 —— 关闭键在浮层里的**落位**。 + * + * 这一族在网页端有两条同名规则:6874 给基础外观,这条给「贴在浮层右上角」的绝对定位。 + * 只搬前一条时按钮会掉进网格流、独自占一行并居中(客户端验收现场那张图里的 ×)。 + */ +.image-canvas-editor__generation-close { + position: absolute; + right: 0.62rem; + top: 0.58rem; + width: 2rem; + height: 2rem; + color: #94a3b8; +} + +/* src/index.css:16988:composer 里的参数簇去掉自身底色,hover / 展开时才浮起。 */ +.image-canvas-editor__generation-composer .image-canvas-editor__option-cluster { + min-height: 2.25rem; + background: transparent; + border-color: transparent; +} + +/* src/index.css:16994 */ +.image-canvas-editor__generation-composer + .image-canvas-editor__option-cluster:hover:not(:disabled), +.image-canvas-editor__generation-composer + .image-canvas-editor__option-cluster[aria-expanded='true'] { + border-color: rgba(15, 23, 42, 0.08); + background: #f8fafc; + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); +} + +/* + * 窄屏整档照抄(src/index.css:8981 的 `@media (max-width: 760px)`):浮层落到底部整宽、 + * 动作竖排、参数与提交整宽。选择器与断点都按网页端原样搬,避免"我拆一半、你拆一半"之后 + * 两边再各自漂移。 + */ +@media (max-width: 760px) { + /* src/index.css:9020 */ + .image-canvas-editor__quick-edit-panel, .image-canvas-editor__character-animation-panel { position: fixed; left: 0.75rem !important; @@ -1732,11 +1794,41 @@ button.image-canvas-editor__reference-chip:disabled { transform: none; } + /* src/index.css:9037 */ + .image-canvas-editor__generation-composer { + position: fixed; + left: 0.75rem !important; + right: 0.75rem; + top: auto !important; + bottom: 3.85rem; + width: auto; + max-height: min(72vh, 31rem); + grid-template-columns: 3.55rem minmax(0, 1fr); + overflow: auto; + transform: none; + } + + /* src/index.css:9050 */ + .image-canvas-editor__generation-composer-footer, .image-canvas-editor__character-animation-footer { grid-template-columns: minmax(0, 1fr); align-items: stretch; } + /* src/index.css:9056 */ + .image-canvas-editor__option-popover-anchor--dimensions, + .image-canvas-editor__option-popover-anchor--model { + grid-column: auto; + width: 100%; + justify-self: stretch; + } + + /* src/index.css:9063 */ + .image-canvas-editor__option-cluster--dimensions, + .image-canvas-editor__option-cluster--model, + .image-canvas-editor__image-style-toggle, + .image-canvas-editor__readonly-generation-option, + .image-canvas-editor__generation-submit, .image-canvas-editor__character-animation-submit { grid-column: auto; width: 100%; diff --git a/apps/ai-game-creator-shell/tests/resourceCanvasCharacterAnimationPanelStyle.test.tsx b/apps/ai-game-creator-shell/tests/resourceCanvasCharacterAnimationPanelStyle.test.tsx index 9c02e4768..461cda797 100644 --- a/apps/ai-game-creator-shell/tests/resourceCanvasCharacterAnimationPanelStyle.test.tsx +++ b/apps/ai-game-creator-shell/tests/resourceCanvasCharacterAnimationPanelStyle.test.tsx @@ -112,6 +112,29 @@ function readRules(file: string): CssRule[] { return rules; } +/** + * 选择器能不能命中这一份 DOM:逐条 `matches()` 试。 + * + * 只做「元素是否可能匹配」这一层,所以把状态类伪类(`:hover` / `:focus-within` / `:disabled`…) + * 与伪元素摘掉再试;`::before` / `::after` 这类由真实浏览器生成的内容不参与判断。 + */ +function ruleMatchesPanel(selector: string, elements: readonly Element[]) { + const stripped = selector + .replace(/::[a-z-]+/gu, '') + .replace(/:[a-z-]+(\([^)]*\))?/gu, '') + .trim(); + if (!stripped) { + return false; + } + return elements.some((element) => { + try { + return element.matches(stripped); + } catch { + return false; + } + }); +} + function declarationOf( rules: readonly CssRule[], className: string, @@ -287,4 +310,47 @@ describe('「生成动画」面板的 AGC 样式覆盖', () => { declarationOf(rules, 'image-canvas-editor__reference-chip', 'width'), ).toBe('3.95rem'); }); + + it('网页端命中这块面板的每条规则,AGC 都照抄了一条同名规则', () => { + const container = animationPanel(); + const elements = [ + container, + ...Array.from(container.querySelectorAll('*')), + ]; + + /* + 只守画布组件这一族(`image-canvas-editor__*`):共享 chrome 类(`.platform-button` 等) + 与全局 reset 本来就不归宿主 chrome 表照抄——它们活在共享样式表里,宿主引入与否是另一件事。 + */ + const matched = readRules(resolve(REPO_ROOT, 'src/index.css')).filter( + (rule) => + rule.selector.includes('image-canvas-editor__') && + rule.selector + .split(',') + .some((selector) => ruleMatchesPanel(selector, elements)), + ); + expect( + matched.length, + '一条规则都没匹配上:判据退化成空集,守卫失效', + ).toBeGreaterThan(10); + + /* + 比对键是**选择器 + 声明**而不是选择器:同名规则在网页端出现两次时(例如 + `.image-canvas-editor__generation-close` 先给基础外观、再给浮层内的绝对定位), + 只按选择器比对会让「只搬了第一条」判成已覆盖——这正是关闭键掉进网格流、跑到面板正中的原因。 + */ + const ruleKey = (rule: CssRule) => `${rule.selector} { ${rule.body} }`; + const agcRuleKeys = new Set( + collectAgcLoadedStyleSheets().flatMap(readRules).map(ruleKey), + ); + const missing = [ + ...new Set( + matched.filter((rule) => !agcRuleKeys.has(ruleKey(rule))).map(ruleKey), + ), + ]; + expect( + missing, + `这些规则网页端会命中动画面板、AGC 一条都没有:\n${missing.join('\n')}`, + ).toEqual([]); + }); });