feat: open compose editor in edit mode with one click (#1592)

* feat: open compose editor in edit mode with one click

Remove the desktop double edit gate so Anatomy "Edit compose" and empty-stack
create land in an immediately editable workspace. Close discards both compose
and env buffers; deferred loads keep startInComposeEdit through unsaved confirm.

* docs: align compose editor close behavior and Edit compose labels

Update stack-management and editor docs for one-click edit mode.

Close discards unsaved compose/env edits. Anatomy shortcuts use Edit compose.

* fix(e2e): update routing test to use anatomy-edit-compose-btn test id

The compose editor env tab routing test was written against the old
'edit' button name. The single-compose-edit-gate branch renamed that
button to 'Edit compose' with test id 'anatomy-edit-compose-btn'.
Update the selector to match.
This commit is contained in:
Anso
2026-07-09 13:53:26 -04:00
committed by GitHub
parent d113004359
commit e2001fed72
16 changed files with 463 additions and 95 deletions
+15 -4
View File
@@ -233,6 +233,10 @@ export default function EditorLayout() {
getLastDeployOutputLine,
diffPreviewEnabled,
hasUpdateGuard: hasCapability('update-guard'),
canEditStack: (stackNameOrFilename) => {
const stackName = stackNameOrFilename.replace(/\.(ya?ml)$/, '');
return can('stack:edit', 'stack', stackName);
},
canOfferVolumeRemoval,
});
@@ -543,7 +547,8 @@ export default function EditorLayout() {
updateStack={stackActions.updateStack}
rollbackStack={stackActions.rollbackStack}
scanStackConfig={stackActions.scanStackConfig}
enterEditMode={stackActions.enterEditMode}
openComposeEditor={stackActions.openComposeEditor}
closeComposeEditor={stackActions.closeComposeEditor}
requestSave={stackActions.requestSave}
requestSaveAndDeploy={stackActions.requestSaveAndDeploy}
discardChanges={stackActions.discardChanges}
@@ -573,7 +578,7 @@ export default function EditorLayout() {
onDismissRecovery={() => { if (selectedFile) dismissActionResult(selectedFile); }}
panelStartedAt={panelStartedAt}
onMobileBack={goToMobileList}
onCloseEditor={() => stackActions.attemptLeaveEditor(() => setEditingCompose(false))}
onCloseEditor={() => stackActions.attemptLeaveEditor(() => stackActions.closeComposeEditor())}
hasUnsavedChanges={stackActions.hasUnsavedChanges}
stackMuteActions={selectedFile ? stackMuteActions : undefined}
/>
@@ -619,6 +624,7 @@ export default function EditorLayout() {
// effect fire a no-op so dirty content survives.
overlayState.setPendingUnsavedNode(activeNode);
overlayState.setPendingUnsavedLoad(NODE_SWITCH_PENDING_TOKEN);
overlayState.setPendingLoadOptions(null);
revertingNodeSwitchRef.current = true;
setActiveNode(previousNode);
return;
@@ -685,7 +691,7 @@ export default function EditorLayout() {
open={createDialogOpen}
onOpenChange={setCreateDialogOpen}
initialMode={createDialogInitialMode}
onStackCreated={async (sName, sourceNodeId) => {
onStackCreated={async (sName, sourceNodeId, meta) => {
await refreshStacks();
// loadFile keeps its own unsaved-changes overlay (intentional safety,
// shared with every other "switch to a different stack" code path).
@@ -695,7 +701,12 @@ export default function EditorLayout() {
toast.info(`Stack "${sName}" created on the previous node.`);
return;
}
await stackActions.loadFile(sName);
// Empty creates land in an editable compose workspace. Other modes
// (git, docker-run, import) stay browse-first on Anatomy.
await stackActions.loadFile(
sName,
meta?.mode === 'empty' ? { startInComposeEdit: true } : undefined,
);
}}
onStacksChanged={async () => { await refreshStacks(); }}
/>