mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix: preserve Scene shot labels for double-click rename
This commit is contained in:
1 parent
9b6d34cb8a
commit
1f76f81484
6 files changed
+65
-3
No files matched your search
@@ -14,6 +14,7 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md
|
||||
## [Unreleased]
|
||||
|
||||
- Separate Scene controls and text presentation from project/playback operations; revoke replaced row listeners and suppress stale completion feedback.
|
||||
- Preserve shot-label identity on selection so double-click rename can complete.
|
||||
|
||||
- Split Cockpit camera/controller, instruments, briefing, signals and layout into focused modules with explicit application operations.
|
||||
- Give Display portal moves cancellable focus/scroll restoration and stop Cockpit work before asynchronous UI teardown.
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
Scene controls own creation/deletion prompts, panel listeners, shot rows, playback/recording presentation
|
||||
and keyboard cancellation. The director supplies project reads and explicit
|
||||
editing/playback actions while retaining persistence, camera and layer sequencing.
|
||||
Replacing rows revokes their old listeners. Disposal stops controls immediately;
|
||||
Shot selection updates the highlight without replacing the row, preserving
|
||||
native double-click rename. Replacing rows revokes their old listeners. Disposal stops controls immediately;
|
||||
late file and failed-action completions cannot update removed presentation.
|
||||
|
||||
## Cockpit component ownership
|
||||
|
||||
@@ -409,7 +409,7 @@ export class SceneDirector {
|
||||
this._selectedShotId = this._getSelectedScene()?.shots[0]?.id || null;
|
||||
this._renderShotList();
|
||||
},
|
||||
selectShot: (id) => { this._selectedShotId = id; this._renderShotList(); },
|
||||
selectShot: (id) => { this._selectedShotId = id; },
|
||||
renameShot: (sceneId, shotId, title) => {
|
||||
const { shot } = this._getShot(sceneId, shotId);
|
||||
if (!shot) return;
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
sceneElements,
|
||||
renderSceneOptions,
|
||||
renderSceneShots,
|
||||
presentSceneSelection,
|
||||
presentSceneButtons,
|
||||
presentSceneProgress,
|
||||
presentSceneRuntime,
|
||||
@@ -112,7 +113,14 @@ export class SceneControls {
|
||||
renderSceneShots(this.elements.shots, this.read(), {
|
||||
listen: (element, type, callback) =>
|
||||
this.listen(element, type, callback, this.rowRemovers),
|
||||
select: (id) => this.run('selectShot', id),
|
||||
select: (id) => {
|
||||
this.run('selectShot', id);
|
||||
if (!this.destroyed)
|
||||
presentSceneSelection(
|
||||
this.elements.shots,
|
||||
this.read().selectedShotId,
|
||||
);
|
||||
},
|
||||
rename: (sceneId, shotId, title) =>
|
||||
this.run('renameShot', sceneId, shotId, title),
|
||||
load: (sceneId, shotId) => this.run('load', sceneId, shotId),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { SceneControls } from './sceneControls.js';
|
||||
import { SceneDirector } from '../scenes/director.js';
|
||||
|
||||
function fixture() {
|
||||
class Element extends EventTarget {
|
||||
@@ -8,6 +9,7 @@ function fixture() {
|
||||
super();
|
||||
this.children = [];
|
||||
this.style = {};
|
||||
this.dataset = {};
|
||||
this.value = '';
|
||||
this._text = '';
|
||||
const classes = new Set();
|
||||
@@ -304,3 +306,46 @@ test('creation and deletion prompts precede their model actions and remain inert
|
||||
f.restore();
|
||||
}
|
||||
});
|
||||
|
||||
test('the real director preserves a selected shot label for the following double-click', async () => {
|
||||
const f = fixture();
|
||||
const previousStorage = globalThis.localStorage;
|
||||
let director;
|
||||
try {
|
||||
f.owner.destroy();
|
||||
const ids = {
|
||||
'scene-panel': 'panel',
|
||||
'scene-select': 'select',
|
||||
'scene-new-btn': 'new',
|
||||
'scene-delete-btn': 'delete',
|
||||
'scene-capture-btn': 'capture',
|
||||
'scene-update-shot-btn': 'update',
|
||||
'scene-shot-list': 'shots',
|
||||
'scene-start-btn': 'start',
|
||||
'scene-stop-btn': 'stop',
|
||||
'scene-next-btn': 'next',
|
||||
'scene-export-btn': 'export',
|
||||
'scene-import-btn': 'import',
|
||||
'scene-import-file': 'file',
|
||||
'scene-download-btn': 'download',
|
||||
'scene-status': 'status',
|
||||
'scene-progress-fill': 'progress',
|
||||
'scene-runtime': 'runtime',
|
||||
};
|
||||
f.document.getElementById = (id) => f.elements[ids[id]] || null;
|
||||
globalThis.localStorage = {
|
||||
getItem: () => JSON.stringify({ version: 3, scenes: f.state.scenes }),
|
||||
setItem() {},
|
||||
};
|
||||
director = new SceneDirector({ camera: { cancelFlight() {} } }, {}, {});
|
||||
const label = f.elements.shots.children[0].children[0].children[0];
|
||||
label.click();
|
||||
assert.equal(f.elements.shots.children[0].children[0].children[0], label);
|
||||
label.dispatchEvent(new Event('dblclick'));
|
||||
assert.equal(director._getSelectedScene().shots[0].title, 'Renamed');
|
||||
} finally {
|
||||
await director?.destroy();
|
||||
globalThis.localStorage = previousStorage;
|
||||
f.restore();
|
||||
}
|
||||
});
|
||||
@@ -54,6 +54,7 @@ export function renderSceneShots(
|
||||
for (const shot of scene.shots) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'scene-shot-row';
|
||||
row.dataset.sceneShotId = shot.id;
|
||||
row.classList.toggle('active', shot.id === state.selectedShotId);
|
||||
const top = document.createElement('div');
|
||||
top.className = 'scene-shot-top';
|
||||
@@ -90,6 +91,12 @@ export function renderSceneShots(
|
||||
}
|
||||
}
|
||||
|
||||
export function presentSceneSelection(element, selectedShotId) {
|
||||
for (const row of element?.children || []) {
|
||||
row.classList.toggle('active', row.dataset.sceneShotId === selectedShotId);
|
||||
}
|
||||
}
|
||||
|
||||
export function presentSceneButtons(elements, running, hasRun) {
|
||||
for (const name of [
|
||||
'start',
|
||||
|
||||
Reference in new issue
Block a user