按美术画布原样补齐动画面板样式:关闭键落位、提示词态与 760px 整档

- 补 `.image-canvas-editor__generation-close` 的第二条同名规则(浮层内绝对定位):上一版只搬了基础外观,关闭键因此掉进网格流、独自占一行并居中
- 补提示词输入区的聚焦态与 aria-invalid 两档,以及 composer 内参数簇的透明底色与 hover / 展开态
- 新增 `@media (max-width: 760px)` 整档照抄(浮层落底部整宽、composer 定宽、动作竖排、参数与提交整宽),替换上一版按 720px 手写的那一小段,选择器与断点都与网页端逐字一致
- 守卫升级为「选择器 + 声明」逐条比对:同名规则只搬第一条不再判过;范围限定在 `image-canvas-editor__` 命名空间(共享 chrome 类与全局 reset 不归宿主 chrome 表照抄)
This commit is contained in:
2026-09-21 00:26:31 +08:00
parent 973e965f4f
commit 5b4f9e961d
2 changed files with 160 additions and 2 deletions
@@ -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%;
@@ -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([]);
});
});