refactor: keep Scene prompts with their controls

This commit is contained in:
Sameh Khamis committed 2026-09-13 03:35:22 -07:00
1 parent d42036d74f
commit 9b6d34cb8a
5 files changed
+77 -15

No files matched your search

+3 -10
View File
@@ -417,7 +417,7 @@ export class SceneDirector {
this._saveProject();
this._renderShotList();
},
create: () => this._createScene(),
create: (name) => this._createScene(name),
deleteScene: () => this._deleteSelectedScene(),
capture: () => this.captureShot(),
update: () => this.updateSelectedShot(),
@@ -474,9 +474,8 @@ export class SceneDirector {
return { scene, shot };
}
/** Prompt the user for a name and append a new empty scene to the project. */
_createScene() {
const sceneName = window.prompt('New scene name', `Scene ${this._project.scenes.length + 1}`);
/** Append a named empty scene after the controls accept the creation prompt. */
_createScene(sceneName) {
if (!sceneName) return;
const scene = {
@@ -498,9 +497,6 @@ export class SceneDirector {
const scene = this._getSelectedScene();
if (!scene) return;
const ok = window.confirm(`Delete scene "${scene.title}" and all shots?`);
if (!ok) return;
this._project.scenes = this._project.scenes.filter((item) => item.id !== scene.id);
// Restore default recipes if the user deleted all scenes
if (!this._project.scenes.length) {
@@ -596,9 +592,6 @@ export class SceneDirector {
const { scene, shot } = this._getShot(sceneId, shotId);
if (!scene || !shot) return;
const ok = window.confirm(`Delete shot "${shot.title}"?`);
if (!ok) return;
scene.shots = scene.shots.filter((item) => item.id !== shot.id);
this._selectedShotId = scene.shots[0]?.id || null;
this._saveProject();
+30 -3
View File
@@ -25,8 +25,6 @@ export class SceneControls {
this.run('selectScene', elements.select.value),
);
for (const [element, action] of Object.entries({
new: 'create',
delete: 'deleteScene',
capture: 'capture',
update: 'update',
next: 'next',
@@ -35,6 +33,8 @@ export class SceneControls {
})) {
this.listen(elements[element], 'click', () => this.run(action));
}
this.listen(elements.new, 'click', () => this.createScene());
this.listen(elements.delete, 'click', () => this.deleteSelectedScene());
this.listen(elements.start, 'click', () =>
this.run('start', this.read().selectedSceneId),
);
@@ -79,6 +79,33 @@ export class SceneControls {
if (!this.destroyed) renderSceneOptions(this.elements.select, this.read());
}
createScene() {
if (this.destroyed) return;
const name = window.prompt(
'New scene name',
`Scene ${this.read().scenes.length + 1}`,
);
if (name) this.run('create', name);
}
deleteSelectedScene() {
if (this.destroyed) return;
const state = this.read();
const scene = state.scenes.find(
(item) => item.id === state.selectedSceneId,
);
if (scene && window.confirm(`Delete scene "${scene.title}" and all shots?`))
this.run('deleteScene');
}
deleteShot(sceneId, shotId) {
if (this.destroyed) return;
const scene = this.read().scenes.find((item) => item.id === sceneId);
const shot = scene?.shots.find((item) => item.id === shotId);
if (shot && window.confirm(`Delete shot "${shot.title}"?`))
this.run('deleteShot', sceneId, shotId);
}
renderShotList() {
if (this.destroyed) return;
for (const remove of this.rowRemovers.splice(0)) remove();
@@ -89,7 +116,7 @@ export class SceneControls {
rename: (sceneId, shotId, title) =>
this.run('renameShot', sceneId, shotId, title),
load: (sceneId, shotId) => this.run('load', sceneId, shotId),
remove: (sceneId, shotId) => this.run('deleteShot', sceneId, shotId),
remove: (sceneId, shotId) => this.deleteShot(sceneId, shotId),
});
}
+42
View File
@@ -262,3 +262,45 @@ test('Scene action failures are visible only while that action still owns presen
f.restore();
}
});
test('creation and deletion prompts precede their model actions and remain inert after disposal', () => {
const f = fixture();
try {
let prompts = 0;
window.prompt = () => {
prompts++;
return null;
};
window.confirm = () => {
prompts++;
return false;
};
f.elements.new.click();
f.elements.delete.click();
f.owner.deleteShot('scene-a', 'shot-a');
assert.deepEqual(f.calls, []);
window.prompt = () => {
prompts++;
return 'New title';
};
window.confirm = () => {
prompts++;
return true;
};
f.elements.new.click();
f.elements.delete.click();
f.owner.deleteShot('scene-a', 'shot-a');
assert.deepEqual(f.calls, [
['create', 'New title'],
['deleteScene'],
['deleteShot', 'scene-a', 'shot-a'],
]);
f.owner.destroy();
f.owner.createScene();
f.owner.deleteSelectedScene();
f.owner.deleteShot('scene-a', 'shot-a');
assert.equal(prompts, 6);
} finally {
f.restore();
}
});