From a254dfbcd97dea2987e6f566f5c90e9d6b4b5d15 Mon Sep 17 00:00:00 2001 From: shimmyshimmer Date: Sat, 25 Jul 2026 02:42:46 -0700 Subject: [PATCH] Images: drop card borders for the composer shadow, keep scrollbars inside, use app controls in Train - Cards lose ring-1 for .panel-soft-surface: the composer's shadow in light, flat in dark, matching .chat-composer-surface and the menus. - Both rails now clip (overflow-hidden) with the scroller inside, so the scrollbar can't ride over the rounded corner. Same shape video-page already uses. - Train's 9 native selects become the app Select, so they no longer open an OS-native menu, and the native file input is hidden behind a Choose images button that reports the count. - Image previews use explicit 8-10px radii: this theme sets --radius to 1.1rem, so rounded-md was 15.6px and the thumbnails read as circles. --- .../src/features/images/images-page.tsx | 1030 +++++++++-------- .../images/train/dataset-labeling-grid.tsx | 2 +- .../images/train/dataset-showcase.tsx | 2 +- .../images/train/diffusion-train-panel.tsx | 603 +++++----- .../images/train/example-dataset-cards.tsx | 2 +- studio/frontend/src/index.css | 10 + 6 files changed, 857 insertions(+), 792 deletions(-) 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" && ( - - - - )} - - -