diff --git a/studio/frontend/src/features/images/images-page.tsx b/studio/frontend/src/features/images/images-page.tsx index c3f31232fd..be6b913fb4 100644 --- a/studio/frontend/src/features/images/images-page.tsx +++ b/studio/frontend/src/features/images/images-page.tsx @@ -606,7 +606,7 @@ function ImageDropzone({ if (value) { return ( -
+
Source - - {/* DropdownMenuContent already tracks the trigger width, so the rows get - the full rail width for their hints. */} - - {WORKFLOW_TABS.map((t) => { - const enabled = workflowEnabled(t); - return ( - setWorkflow(t.id)} - // The selected row's description shows under the trigger, and a - // disabled row explains itself on hover, so rows stay one line. - title={ - enabled - ? t.hint - : `Needs a loaded model that supports ${t.label.toLowerCase()}` - } - className="gap-2" - > - - - {t.label} - - - - ); - })} - - - {/* Description sits under the closed trigger, matching the Field hints - below it, so the button itself stays one line. */} -

- {activeWorkflowTab.hint} -

-
- - {workflow === "transform" && ( - <> - - - - - - )} - - {workflow === "inpaint" && ( - <> - {!initImage ? ( - - - - ) : ( - <> - +
+ {/* Workflow picker. Seven workflows don't fit a segmented strip in a 340px + rail, so it's a dropdown: the trigger carries the current workflow and + its hint, and each row explains itself. A row is disabled until the + loaded model supports it (status.workflows), with the reason in place of + the hint. New workflows slot in without shrinking anything. */} +
+ + + - -
- - - )} - - )} - - {workflow === "extend" && ( - <> - - - - - -
- {( - [ - ["top", "Top"], - ["bottom", "Bottom"], - ["left", "Left"], - ["right", "Right"], - ] as Array<[keyof ExtendSides, string]> - ).map(([key, label]) => { - const on = extendSides[key]; + + {activeWorkflowTab.label} + + + + + {/* DropdownMenuContent already tracks the trigger width, so the rows get + the full rail width for their hints. */} + + {WORKFLOW_TABS.map((t) => { + const enabled = workflowEnabled(t); return ( - + + + {t.label} + + + ); })} -
-
- - )} + + + {/* Description sits under the closed trigger, matching the Field hints + below it, so the button itself stays one line. */} +

+ {activeWorkflowTab.hint} +

+
- {workflow === "upscale" && ( - <> - - - - - - - )} - - {workflow === "reference" && ( - <> - - - - {referenceImages.map((img, i) => ( + {workflow === "transform" && ( + <> -
- - // Keep the slot in place (empty string when cleared) so other slots - // don't renumber mid-edit; empty slots are dropped only at send time - // and removed explicitly via the button below. Stable key={i}. - setReferenceImages((prev) => - prev.map((p, j) => (j === i ? (v ?? "") : p)), - ) - } - /> - + + + +
+ + +
+ + + )} + + )} + + {workflow === "extend" && ( + <> + + + + + +
+ {( + [ + ["top", "Top"], + ["bottom", "Bottom"], + ["left", "Left"], + ["right", "Right"], + ] as Array<[keyof ExtendSides, string]> + ).map(([key, label]) => { + const on = extendSides[key]; + return ( + + ); + })}
- ))} - {referenceImages.length < 3 && ( - - )} - - )} + + + + + + )} - {workflow === "edit" && ( - - - - )} - - -