mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +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]
|
## [Unreleased]
|
||||||
|
|
||||||
- Separate Scene controls and text presentation from project/playback operations; revoke replaced row listeners and suppress stale completion feedback.
|
- 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.
|
- 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.
|
- 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
|
Scene controls own creation/deletion prompts, panel listeners, shot rows, playback/recording presentation
|
||||||
and keyboard cancellation. The director supplies project reads and explicit
|
and keyboard cancellation. The director supplies project reads and explicit
|
||||||
editing/playback actions while retaining persistence, camera and layer sequencing.
|
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.
|
late file and failed-action completions cannot update removed presentation.
|
||||||
|
|
||||||
## Cockpit component ownership
|
## Cockpit component ownership
|
||||||
|
|||||||
@@ -409,7 +409,7 @@ export class SceneDirector {
|
|||||||
this._selectedShotId = this._getSelectedScene()?.shots[0]?.id || null;
|
this._selectedShotId = this._getSelectedScene()?.shots[0]?.id || null;
|
||||||
this._renderShotList();
|
this._renderShotList();
|
||||||
},
|
},
|
||||||
selectShot: (id) => { this._selectedShotId = id; this._renderShotList(); },
|
selectShot: (id) => { this._selectedShotId = id; },
|
||||||
renameShot: (sceneId, shotId, title) => {
|
renameShot: (sceneId, shotId, title) => {
|
||||||
const { shot } = this._getShot(sceneId, shotId);
|
const { shot } = this._getShot(sceneId, shotId);
|
||||||
if (!shot) return;
|
if (!shot) return;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
sceneElements,
|
sceneElements,
|
||||||
renderSceneOptions,
|
renderSceneOptions,
|
||||||
renderSceneShots,
|
renderSceneShots,
|
||||||
|
presentSceneSelection,
|
||||||
presentSceneButtons,
|
presentSceneButtons,
|
||||||
presentSceneProgress,
|
presentSceneProgress,
|
||||||
presentSceneRuntime,
|
presentSceneRuntime,
|
||||||
@@ -112,7 +113,14 @@ export class SceneControls {
|
|||||||
renderSceneShots(this.elements.shots, this.read(), {
|
renderSceneShots(this.elements.shots, this.read(), {
|
||||||
listen: (element, type, callback) =>
|
listen: (element, type, callback) =>
|
||||||
this.listen(element, type, callback, this.rowRemovers),
|
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) =>
|
rename: (sceneId, shotId, title) =>
|
||||||
this.run('renameShot', sceneId, shotId, title),
|
this.run('renameShot', sceneId, shotId, title),
|
||||||
load: (sceneId, shotId) => this.run('load', sceneId, shotId),
|
load: (sceneId, shotId) => this.run('load', sceneId, shotId),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { SceneControls } from './sceneControls.js';
|
import { SceneControls } from './sceneControls.js';
|
||||||
|
import { SceneDirector } from '../scenes/director.js';
|
||||||
|
|
||||||
function fixture() {
|
function fixture() {
|
||||||
class Element extends EventTarget {
|
class Element extends EventTarget {
|
||||||
@@ -8,6 +9,7 @@ function fixture() {
|
|||||||
super();
|
super();
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.style = {};
|
this.style = {};
|
||||||
|
this.dataset = {};
|
||||||
this.value = '';
|
this.value = '';
|
||||||
this._text = '';
|
this._text = '';
|
||||||
const classes = new Set();
|
const classes = new Set();
|
||||||
@@ -304,3 +306,46 @@ test('creation and deletion prompts precede their model actions and remain inert
|
|||||||
f.restore();
|
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) {
|
for (const shot of scene.shots) {
|
||||||
const row = document.createElement('div');
|
const row = document.createElement('div');
|
||||||
row.className = 'scene-shot-row';
|
row.className = 'scene-shot-row';
|
||||||
|
row.dataset.sceneShotId = shot.id;
|
||||||
row.classList.toggle('active', shot.id === state.selectedShotId);
|
row.classList.toggle('active', shot.id === state.selectedShotId);
|
||||||
const top = document.createElement('div');
|
const top = document.createElement('div');
|
||||||
top.className = 'scene-shot-top';
|
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) {
|
export function presentSceneButtons(elements, running, hasRun) {
|
||||||
for (const name of [
|
for (const name of [
|
||||||
'start',
|
'start',
|
||||||
|
|||||||
Reference in new issue
Block a user