Clarify cue save behavior

This commit is contained in:
vance 2026-04-10 15:56:13 -07:00
parent bd2087ba5f
commit 679044d0b3
3 changed files with 38 additions and 8 deletions

View File

@ -2132,9 +2132,11 @@ export const App = () => {
<div className="section-heading">
<div>
<p className="admin-panel-kicker">Cue draft</p>
<h3>{cueDraft.id ? "Edit armed cue" : "Build new cue from preview"}</h3>
<h3>{cueDraft.id ? "Editing selected cue" : "Unsaved cue draft"}</h3>
</div>
<span className="source-badge">{cueDraft.id ? `${cueDraft.id}${cueDraftDirty ? " / unsaved" : ""}` : "unsaved"}</span>
<span className="source-badge">
{cueDraft.id ? `${cueDraft.id}${cueDraftDirty ? " / unsaved changes" : " / saved"}` : "new cue"}
</span>
</div>
<div className="cue-builder-grid">
<label className="parameter-field">
@ -2240,11 +2242,15 @@ export const App = () => {
</label>
</div>
<div className="cue-builder-actions">
<button onClick={() => void handleNewCueFromPreview()} disabled={!selectedScene || !previewParams || cueMutationInFlight}>
{cueMutationInFlight ? "Working..." : "New"}
<button
onClick={() => void handleNewCueFromPreview()}
disabled={!selectedScene || !previewParams || cueMutationInFlight}
title="Create a separate cue using the current preview settings."
>
{cueMutationInFlight ? "Working..." : "Save as new cue"}
</button>
<button onClick={() => void handleSaveCue()} disabled={!canSaveCue}>
{cueMutationInFlight ? "Working..." : cueDraft.id && !cueDraftDirty ? "Saved" : cueDraft.id ? "Save" : "Save cue"}
{cueMutationInFlight ? "Working..." : cueDraft.id && !cueDraftDirty ? "Saved" : cueDraft.id ? "Save changes" : "Create cue"}
</button>
<button onClick={() => void handleCreateCueAfterCurrent()} disabled={!selectedScene || !previewParams || cueMutationInFlight}>
Insert after

View File

@ -195,7 +195,7 @@ describe("adminReducer library reconciliation", () => {
});
describe("adminReducer preview and operator controls", () => {
it("selects a scene as one clean preview transition", () => {
it("keeps selected cue identity when changing its scene/effect context", () => {
const state = bootState([cue("cue-a", 0, "A")], "cue-a");
const selected = adminReducer(state, {
type: "previewSceneSelected",
@ -211,7 +211,26 @@ describe("adminReducer preview and operator controls", () => {
expect(selected.previewParams).toBe(otherScene.defaultParams);
expect(selected.selectedAssetIds).toEqual(["asset-a", "asset-b"]);
expect(selected.metadataAssetId).toBe("asset-a");
expect(selected.cueDraft.id).toBe("cue-a");
expect(selected.cueDraftDirty).toBe(true);
});
it("selects a scene as a clean draft when no cue is selected", () => {
const state = {
...bootState([cue("cue-a", 0, "A")], "cue-a"),
cueDraft: createCueDraft(undefined, scene),
cueDraftDirty: false
};
const selected = adminReducer(state, {
type: "previewSceneSelected",
scene: otherScene,
presetId: "preset-test",
params: otherScene.defaultParams,
assetIds: ["asset-a"]
});
expect(selected.cueDraft.id).toBeNull();
expect(selected.cueDraft.notes).toBe(otherScene.name);
expect(selected.cueDraftDirty).toBe(false);
});

View File

@ -587,8 +587,13 @@ export const adminReducer = (state: AdminUiState, action: AdminAction): AdminUiS
metadataAssetId: action.assetIds[0] ?? null,
previewParams: action.params,
activePresetId: action.presetId,
cueDraft: createCueDraft(undefined, action.scene),
cueDraftDirty: false
cueDraft: state.cueDraft.id
? {
...state.cueDraft,
notes: state.cueDraft.notes || action.scene.name
}
: createCueDraft(undefined, action.scene),
cueDraftDirty: Boolean(state.cueDraft.id)
};
case "previewCueSelected":