feat(control): история-аккордеон и форма кисти затемнения

Переименовать блок сюжетной линии в «История» и сделать его сворачиваемым. Добавить выбор круглой/квадратной формы для кистей открытия и закрытия с сохранением в штрихах и сбросом при смене инструмента или сцены без затемнения.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-09-02 10:00:10 +08:00
parent 0a9168b320
commit ea718a26df
8 changed files with 288 additions and 30 deletions
@@ -113,7 +113,7 @@ void test('ControlApp: эффекты в пульте, иконки с тулт
instruments !== -1 && fx !== -1 && instruments < fx,
'Блок инструментов должен быть выше эффектов',
);
assert.ok(fx !== -1 && story !== -1 && fx < story, 'Блок эффектов должен быть выше сюжетной линии');
assert.ok(fx !== -1 && story !== -1 && fx < story, 'Блок эффектов должен быть выше истории');
});
void test('ControlApp: чекбокс привязки токенов к сетке', () => {
@@ -128,11 +128,14 @@ void test('ControlApp: чекбокс привязки токенов к сет
assert.ok(css.includes('.snapToGrid'));
});
void test('ControlApp: сюжетная линия — колонка сверху вниз и фон как у карточек ветвления', () => {
void test('ControlApp: история — колонка сверху вниз и фон как у карточек ветвления', () => {
const src = readControlApp();
const css = readControlAppCss();
const story = src.indexOf("t('control.storyLine')");
assert.ok(story !== -1);
assert.ok(src.includes('data-testid="control-history-accordion"'));
assert.ok(src.includes('aria-expanded={historyOpen}'));
assert.ok(src.includes('const [historyOpen, setHistoryOpen] = useState(true)'));
assert.ok(src.includes('className={styles.storyScroll}'));
assert.match(css, /\.storyScroll[\s\S]*?justify-content:\s*flex-start/);
assert.match(css, /\.storyScroll[\s\S]*?background:\s*var\(--color-overlay-dark-2\)/);
@@ -173,6 +176,27 @@ void test('ControlApp: слой кисти не использует курсо
assert.match(css, /\.brushLayer[\s\S]*?cursor:\s*crosshair/);
});
void test('ControlApp: форма кисти затемнения — UI, shape в штрихах и сброс', () => {
const src = readControlApp();
const css = readControlAppCss();
assert.ok(src.includes("t('control.brushShape')"));
assert.ok(src.includes("t('control.brushShapeCircle')"));
assert.ok(src.includes("t('control.brushShapeSquare')"));
assert.ok(src.includes('darknessBrushShape'));
assert.ok(src.includes('darknessBrushDraft'));
assert.ok(src.includes("shape: darknessBrushShapeRef.current"));
assert.ok(src.includes('setDarknessBrushShape'));
assert.ok(src.includes('styles.brushShapeRow'));
assert.ok(src.includes("tool.tool === 'exploreBrush' || tool.tool === 'closeBrush'"));
assert.ok(css.includes('.brushCursorSquare'));
assert.ok(css.includes('.brushShapeRow'));
const radius = src.indexOf("t('control.brushRadius')");
const shape = src.indexOf("t('control.brushShape')");
const sound = src.indexOf("t('control.effectsSound')");
assert.ok(radius !== -1 && shape !== -1 && sound !== -1);
assert.ok(radius < shape && shape < sound, 'Форма кисти между радиусом и звуком эффектов');
});
void test('ControlApp: радиус кисти не в блоке предпросмотра', () => {
const src = readControlApp();
const previewLabel = src.indexOf("t('control.screenPreview')");