Address review: playable PLAY/NEXT, visible rename editor, accurate copy

- PLAY and NEXT continue into the following scenes, so they stay
  enabled while any scene in the project has a shot, not only the
  selected one.
- Renaming a shot drops the number and ellipsis so the editor is no
  longer hidden; qa-scene-controls checks a long rename stays visible.
- PLAY, NEXT, EDIT DETAILS and SHARE SCENE tooltips describe what the
  buttons do, translated in every shipped language.
- The primary actions and the file row keep a hover state.
- Docs: the EXPORT rename in DIRECTOR-SHARING and the playback button
  states in CURRENT-STATE are accurate.
- Test the voice "authoring" alias through set_panel_open.
This commit is contained in:
Said Turksever committed 2026-10-29 18:39:53 +00:00
1 parent 8de75b172a
commit 5a80facf8d
14 files changed
+129 -34

No files matched your search

+4 -4
View File
@@ -3013,13 +3013,13 @@
"Replace the selected shot with the current view": "Sustituye el plano seleccionado por la vista actual",
"Click a shot to select it, double-click to rename.": "Haz clic en un plano para seleccionarlo y doble clic para cambiarle el nombre.",
"scene|PLAY": "REPROD.",
"Play the whole scene from its first shot": "Reproduce toda la escena desde su primer plano",
"Stop playback (Esc)": "Detén la reproducción (Esc)",
"Play only the shot after the selected one": "Reproduce solo el plano posterior al seleccionado",
"SAVE & SHARE": "GUARDAR Y COMPARTIR",
"Download every scene as a JSON file you can import later": "Descarga todas las escenas como un archivo JSON que puedes importar más tarde",
"Load scenes from an exported JSON file": "Carga escenas desde un archivo JSON exportado",
"Download the timing log of the last play": "Descarga el registro de tiempos de la última reproducción",
"Edit the scene title and description": "Edita el título y la descripción de la escena",
"Share this scene as a link or file": "Comparte esta escena como enlace o archivo"
"Play from this scene through the rest of the project": "Reproduce desde esta escena hasta el resto del proyecto",
"Play the shot after the selected one, moving on to the next scene when needed": "Reproduce el plano posterior al seleccionado y pasa a la siguiente escena si hace falta",
"Edit anchors, data packs and shot details": "Edita anclas, paquetes de datos y detalles de los planos",
"Download this scene as JSON or an asset bundle": "Descarga esta escena como JSON o como paquete de recursos"
}
+4 -4
View File
@@ -3082,13 +3082,13 @@
"Replace the selected shot with the current view": "Заменить выбранный кадр текущим видом",
"Click a shot to select it, double-click to rename.": "Щёлкните кадр, чтобы выбрать его, дважды — чтобы переименовать.",
"scene|PLAY": "ПУСК",
"Play the whole scene from its first shot": "Воспроизвести всю сцену с первого кадра",
"Stop playback (Esc)": "Остановить воспроизведение (Esc)",
"Play only the shot after the selected one": "Воспроизвести только кадр после выбранного",
"SAVE & SHARE": "СОХРАНИТЬ И ПОДЕЛИТЬСЯ",
"Download every scene as a JSON file you can import later": "Скачать все сцены файлом JSON для последующего импорта",
"Load scenes from an exported JSON file": "Загрузить сцены из экспортированного файла JSON",
"Download the timing log of the last play": "Скачать журнал таймингов последнего воспроизведения",
"Edit the scene title and description": "Изменить название и описание сцены",
"Share this scene as a link or file": "Поделиться сценой ссылкой или файлом"
"Play from this scene through the rest of the project": "Воспроизвести с этой сцены и далее по всему проекту",
"Play the shot after the selected one, moving on to the next scene when needed": "Воспроизвести кадр после выбранного, при необходимости перейдя к следующей сцене",
"Edit anchors, data packs and shot details": "Изменить якоря, пакеты данных и параметры кадров",
"Download this scene as JSON or an asset bundle": "Скачать эту сцену в формате JSON или архивом ресурсов"
}
+4 -4
View File
@@ -2875,13 +2875,13 @@
"Replace the selected shot with the current view": "用当前视角替换所选镜头",
"Click a shot to select it, double-click to rename.": "单击镜头即可选中,双击可重命名。",
"scene|PLAY": "播放",
"Play the whole scene from its first shot": "从第一个镜头开始播放整个场景",
"Stop playback (Esc)": "停止播放(Esc)",
"Play only the shot after the selected one": "仅播放所选镜头的下一个镜头",
"SAVE & SHARE": "保存与分享",
"Download every scene as a JSON file you can import later": "将所有场景下载为 JSON 文件,以便日后导入",
"Load scenes from an exported JSON file": "从导出的 JSON 文件加载场景",
"Download the timing log of the last play": "下载上次播放的计时日志",
"Edit the scene title and description": "编辑场景标题和描述",
"Share this scene as a link or file": "以链接或文件形式分享此场景"
"Play from this scene through the rest of the project": "从此场景开始播放项目中的其余场景",
"Play the shot after the selected one, moving on to the next scene when needed": "播放所选镜头的下一个镜头,必要时进入下一个场景",
"Edit anchors, data packs and shot details": "编辑锚点、数据包和镜头详情",
"Download this scene as JSON or an asset bundle": "将此场景下载为 JSON 或资源包"
}
+4 -4
View File
@@ -2875,13 +2875,13 @@
"Replace the selected shot with the current view": "用當前視角替換所選鏡頭",
"Click a shot to select it, double-click to rename.": "單擊鏡頭即可選中,雙擊可重命名。",
"scene|PLAY": "播放",
"Play the whole scene from its first shot": "從第一個鏡頭開始播放整個場景",
"Stop playback (Esc)": "停止播放(Esc)",
"Play only the shot after the selected one": "僅播放所選鏡頭的下一個鏡頭",
"SAVE & SHARE": "保存與分享",
"Download every scene as a JSON file you can import later": "將所有場景下載為 JSON 文件,以便日後導入",
"Load scenes from an exported JSON file": "從導出的 JSON 文件加載場景",
"Download the timing log of the last play": "下載上次播放的計時日誌",
"Edit the scene title and description": "編輯場景標題和描述",
"Share this scene as a link or file": "以鏈接或文件形式分享此場景"
"Play from this scene through the rest of the project": "從此場景開始播放項目中的其餘場景",
"Play the shot after the selected one, moving on to the next scene when needed": "播放所選鏡頭的下一個鏡頭,必要時進入下一個場景",
"Edit anchors, data packs and shot details": "編輯錨點、數據包和鏡頭詳情",
"Download this scene as JSON or an asset bundle": "將此場景下載為 JSON 或資源包"
}
+5 -1
View File
@@ -220,7 +220,11 @@ export class SceneControls {
const shots = scene?.shots ?? [];
presentSceneButtons(this.elements, running, state.hasRun, {
hasScene: !!scene,
shotCount: shots.length,
// PLAY and NEXT run on into the following scenes.
playableShots: (state.scenes ?? []).reduce(
(total, item) => total + (item.shots?.length ?? 0),
0,
),
hasSelectedShot: shots.some((shot) => shot.id === state.selectedShotId),
});
this.elements.panel?.classList.toggle('has-shots', shots.length > 0);
+9
View File
@@ -184,7 +184,16 @@ test('Authoring disables idle actions that would do nothing', () => {
assert.equal(f.elements.update.disabled, true);
assert.equal(f.elements.start.disabled, false);
// PLAY and NEXT run on into the following scenes, so an empty selected
// scene beside one with shots keeps them available.
f.state.scenes[0].shots = [];
f.state.scenes.push({ id: 'scene-b', title: 'B', shots: [{ id: 'b1' }] });
f.owner.setButtons(false);
assert.equal(f.elements.start.disabled, false);
assert.equal(f.elements.next.disabled, false);
assert.equal(f.elements.update.disabled, true);
f.state.scenes.pop();
f.owner.setButtons(false);
assert.equal(f.elements.start.disabled, true);
assert.equal(f.elements.next.disabled, true);
+8 -6
View File
@@ -173,22 +173,24 @@ export function presentSceneSelection(element, selectedShotId) {
}
/**
* Everything but STOP is off while a scene plays. Otherwise a button is off
* when it would do nothing: DELETE and CAPTURE SHOT without a scene, UPDATE
* SHOT without a selected shot, PLAY and NEXT on a scene with no shots.
* While a scene plays, the authoring and file actions here are off and STOP
* is on. Otherwise a button is off when it would do nothing: DELETE and
* CAPTURE SHOT without a scene, UPDATE SHOT without a selected shot, PLAY and
* NEXT when no scene in the project has a shot (both continue into the
* following scenes).
*/
export function presentSceneButtons(
elements,
running,
hasRun,
{ hasScene = true, shotCount = 1, hasSelectedShot = true } = {},
{ hasScene = true, playableShots = 1, hasSelectedShot = true } = {},
) {
const idleOff = {
delete: !hasScene,
capture: !hasScene,
update: !hasSelectedShot,
start: shotCount === 0,
next: shotCount === 0,
start: playableShots === 0,
next: playableShots === 0,
};
for (const name of [
'start',
+6 -2
View File
@@ -181,8 +181,12 @@ export function mountSceneSharing(
bar.dataset.directorAuthoring = '';
bar.className = 'scene-controls';
const entries = [
[t('EDIT DETAILS'), t('Edit the scene title and description'), edit],
[t('SHARE SCENE'), t('Share this scene as a link or file'), share],
[t('EDIT DETAILS'), t('Edit anchors, data packs and shot details'), edit],
[
t('SHARE SCENE'),
t('Download this scene as JSON or an asset bundle'),
share,
],
];
const removers = [];
for (const [text, title, fn] of entries) {
+18
View File
@@ -223,6 +223,14 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
background: transparent;
}
/* Keep the shared hover on the restyled actions. */
#scene-panel .scene-btn-primary:hover:not(:disabled),
#scene-panel .scene-file-controls .scene-btn:hover:not(:disabled) {
color: var(--accent);
border-color: var(--accent);
background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.scene-shot-hint {
margin: 0 0 8px;
font-size: 11px;
@@ -311,6 +319,16 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
color: var(--text-primary);
}
/* While renaming, the label holds only the editor at full width. */
.scene-shot-label:has(.scene-shot-rename) {
overflow: visible;
white-space: normal;
}
.scene-shot-label:has(.scene-shot-rename)::before {
content: none;
}
/* Shots are numbered 01, 02… in playback order. */
.scene-shot-label::before {
content: counter(scene-shot, decimal-leading-zero);
+2 -2
View File
@@ -166,9 +166,9 @@
<p class="scene-shot-hint" data-i18n>Click a shot to select it, double-click to rename.</p>
<p class="scene-step-heading" data-i18n>PREVIEW</p>
<div class="scene-controls">
<button id="scene-start-btn" type="button" class="gev-action-button scene-btn scene-btn-primary" title="Play the whole scene from its first shot" data-i18n-title data-i18n-runtime>PLAY</button>
<button id="scene-start-btn" type="button" class="gev-action-button scene-btn scene-btn-primary" title="Play from this scene through the rest of the project" data-i18n-title data-i18n-runtime>PLAY</button>
<button id="scene-stop-btn" type="button" class="gev-action-button gev-action-button--danger scene-btn scene-btn-danger" title="Stop playback (Esc)" data-i18n data-i18n-title>STOP</button>
<button id="scene-next-btn" type="button" class="gev-action-button scene-btn" title="Play only the shot after the selected one" data-i18n data-i18n-title>NEXT</button>
<button id="scene-next-btn" type="button" class="gev-action-button scene-btn" title="Play the shot after the selected one, moving on to the next scene when needed" data-i18n data-i18n-title>NEXT</button>
</div>
<p class="scene-step-heading" data-i18n>SAVE &amp; SHARE</p>
<div class="scene-controls scene-file-controls">
+23
View File
@@ -3931,3 +3931,26 @@ test('analyst-only lookup distinguishes duplicate names and uses canonical aircr
assert.equal(plane.ok, true);
assert.equal(plane.latitude, 36, 'canonical ID resolves the current position, not old coordinates');
});
test('set_panel_open accepts the Authoring name for the scene panel', async () => {
const calls = [];
const runner = createGevActionRunner({
viewer: {
clock: { onTick: { addEventListener: () => () => {} } },
scene: { canvas: { addEventListener() {}, removeEventListener() {} } },
camera: {
moveEnd: { addEventListener() {} },
positionWC: Cesium.Cartesian3.fromDegrees(-97.7, 30.2, 1000),
},
},
styleManager: {
setPanelCollapsed: (...args) => calls.push(args),
},
dataManager: { layers: new Map(), isEnabled: () => false, getAll: () => [] },
});
for (const panel of ['authoring', 'Authoring', 'scenes']) {
const result = await runner('set_panel_open', { panelId: panel, open: true });
assert.equal(result.panelId, 'scene-panel');
}
assert.deepEqual(calls.at(-1), ['scene-panel', false, { explicit: true }]);
});