Author corrections without baking them into motion
Constant, ramp, and return offsets now append ordinary channel layers to a lane or cel in explicit owner frames. The inspector exposes the commands as one undoable transaction, shows conflicts, and offers removal or retry while preserving generated bases through regeneration. Ordered-stack compatibility is shared by validation, conflict reporting, and regeneration, including adjacent replacement coverage. Cel-sheet gaps and headers select their lane, so commands cannot fall through to another column's stale selection. 437 tests, 5,804 assertions; both browser flows; 56 Django tests; optimized frontend build.
This commit is contained in:
parent
3dbbe285fc
commit
815ce449ea
14 changed files with 806 additions and 60 deletions
|
|
@ -255,10 +255,54 @@ try {
|
|||
assert.deepEqual(placed(s), [[0, 3], [3, 4], [6, 7], [7, 8], [8, 9]],
|
||||
'a command selected in the sheet has the timeline command semantics');
|
||||
assert.equal(s.history.done.length, before.history.done.length + 9);
|
||||
|
||||
// Correction authoring is reachable from the same selection. The range is
|
||||
// in this cel's own frames and Apply is one isolated history transaction.
|
||||
assert(await evaluate(`(() => {
|
||||
const section = [...document.querySelectorAll('.section')]
|
||||
.find(s => s.querySelector('h2')?.textContent.trim() === 'corrections');
|
||||
const label = [...section.querySelectorAll('label')]
|
||||
.find(l => l.textContent.trim().startsWith('offset (degrees)'));
|
||||
const input = label?.querySelector('input');
|
||||
if (!input) return false;
|
||||
const set = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
|
||||
set.call(input, '15');
|
||||
input.dispatchEvent(new Event('input', {bubbles: true}));
|
||||
input.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
return true;
|
||||
})()`), 'rotation correction value is editable');
|
||||
await sleep(100);
|
||||
await click('apply correction');
|
||||
s = await shot();
|
||||
const selectedCel = instances(s).find(n => n.time.at === 0);
|
||||
const rot = Object.values(selectedCel.channels).find(c => c.over?.length);
|
||||
assert.equal(rot.over.length, 1);
|
||||
assert(Math.abs(rot.over[0].values.value - Math.PI / 12) < 1e-9,
|
||||
'the inspector converts the authored degree offset to radians');
|
||||
assert.equal(s.history.done.length, before.history.done.length + 10);
|
||||
|
||||
// A gap targets its column's lane. This catches the stale-selection bug that
|
||||
// only appears once a sheet has more than one lane.
|
||||
await click('timeline');
|
||||
assert.equal(await evaluate('document.querySelectorAll(".tl-cel").length'), 5);
|
||||
await click('+ lane');
|
||||
await click('cel sheet');
|
||||
assert.equal(await evaluate('document.querySelectorAll(".cs-head:not(.cs-frame)").length'), 2);
|
||||
await evaluate(`([...document.querySelectorAll('.cs-cell')].slice(0, 2)
|
||||
.find(c => c.textContent.trim() !== '—')).click()`);
|
||||
await sleep(100);
|
||||
await evaluate(`([...document.querySelectorAll('.cs-cell')].slice(0, 2)
|
||||
.find(c => c.textContent.trim() === '—')).click()`);
|
||||
await sleep(100);
|
||||
await click('overwrite');
|
||||
s = await shot();
|
||||
assert(Object.values(s.clip.symbols.main.nodes)
|
||||
.some(n => n.kind === 'instance' && n.parent !== 'girl' && n.time.at === 0),
|
||||
'clicking a gap selects that column before overwrite');
|
||||
assert.equal(s.history.done.length, before.history.done.length + 12,
|
||||
'correction, lane creation, and overwrite are separate undo steps');
|
||||
assert.equal(errors.length, 0, JSON.stringify(errors));
|
||||
console.log('PASS: lane commands agree from timeline and cel sheet; no server writes');
|
||||
console.log('PASS: lane commands and corrections agree from timeline and cel sheet; no server writes');
|
||||
} finally {
|
||||
if (ws?.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ id: 999999, method: 'Browser.close' }));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue