diff --git a/frontend/apps/editor3d/app/src/pages/PageDraw/PageDraw.tsx b/frontend/apps/editor3d/app/src/pages/PageDraw/PageDraw.tsx
index 6a43936094..d1f31cc0a3 100644
--- a/frontend/apps/editor3d/app/src/pages/PageDraw/PageDraw.tsx
+++ b/frontend/apps/editor3d/app/src/pages/PageDraw/PageDraw.tsx
@@ -448,20 +448,11 @@ const PageDraw = () => {
items={CANVAS_2D_PAGE_MODEL_LIST}
page={PAGE_ID}
panelTitle="Select Model"
- panelClassName="min-w-[280px]"
+ panelClassName="min-w-[300px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-white/80 hover:text-white"
showIconsInList
triggerLabel="Model"
/>
- {/**/}
diff --git a/frontend/apps/editor3d/app/src/pages/PageEdit/PageEdit.tsx b/frontend/apps/editor3d/app/src/pages/PageEdit/PageEdit.tsx
index d74ae7412b..978f938349 100644
--- a/frontend/apps/editor3d/app/src/pages/PageEdit/PageEdit.tsx
+++ b/frontend/apps/editor3d/app/src/pages/PageEdit/PageEdit.tsx
@@ -470,20 +470,11 @@ const PageEdit = () => {
items={IMAGE_EDITOR_PAGE_MODEL_LIST}
page={PAGE_ID}
panelTitle="Select Model"
- panelClassName="min-w-[280px]"
+ panelClassName="min-w-[300px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-base-fg opacity-80 hover:opacity-100"
showIconsInList
triggerLabel="Model"
/>
- {/*
*/}
diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEditor.tsx b/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEditor.tsx
index 5514dbce3b..bbe1d8ccd0 100644
--- a/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEditor.tsx
+++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEditor.tsx
@@ -548,20 +548,11 @@ export const PageEditor = () => {
items={STAGE_3D_PAGE_MODEL_LIST}
page={PAGE_ID}
panelTitle="Select Model"
- panelClassName="min-w-[280px]"
+ panelClassName="min-w-[300px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-white/80 hover:text-white"
showIconsInList
triggerLabel="Model"
/>
- {/*
*/}
diff --git a/frontend/apps/editor3d/app/src/pages/PageImage/TextToImage.tsx b/frontend/apps/editor3d/app/src/pages/PageImage/TextToImage.tsx
index b2c63a1622..a333c86d28 100644
--- a/frontend/apps/editor3d/app/src/pages/PageImage/TextToImage.tsx
+++ b/frontend/apps/editor3d/app/src/pages/PageImage/TextToImage.tsx
@@ -233,20 +233,11 @@ const TextToImage = ({ imageMediaId, imageUrl }: TextToImageProps) => {
page={PAGE_ID}
mode="hoverSelect"
panelTitle="Select Model"
- panelClassName="min-w-[280px]"
+ panelClassName="min-w-[300px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-white/80 hover:text-white"
showIconsInList
triggerLabel="Model"
/>
- {/*
*/}
diff --git a/frontend/apps/editor3d/app/src/pages/PageVideo/ImageToVideo.tsx b/frontend/apps/editor3d/app/src/pages/PageVideo/ImageToVideo.tsx
index 266b74c170..ecd8d10618 100644
--- a/frontend/apps/editor3d/app/src/pages/PageVideo/ImageToVideo.tsx
+++ b/frontend/apps/editor3d/app/src/pages/PageVideo/ImageToVideo.tsx
@@ -208,20 +208,11 @@ const ImageToVideo = ({ imageMediaId, imageUrl }: ImageToVideoProps) => {
items={IMAGE_TO_VIDEO_PAGE_MODEL_LIST}
page={PAGE_ID}
panelTitle="Select Model"
- panelClassName="min-w-[280px]"
+ panelClassName="min-w-[300px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-white/80 hover:text-white"
showIconsInList
triggerLabel="Model"
/>
- {/*
*/}
diff --git a/frontend/apps/editor3d/app/src/styles/base.css b/frontend/apps/editor3d/app/src/styles/base.css
index 3c9a15153f..f7c227f95e 100644
--- a/frontend/apps/editor3d/app/src/styles/base.css
+++ b/frontend/apps/editor3d/app/src/styles/base.css
@@ -57,12 +57,15 @@ body {
--st-controls-rgb: 62 62 65;
--st-controls-border: #3f3f3f;
--st-fg: #ffffff;
+ --st-fg-rgb: 255 255 255;
--st-divider: #515168;
--st-gallery-vignette-alpha: 0.6;
/* Photo tint (default neutral) */
--st-photo-tint-rgb: 0 0 0;
--st-photo-tint-alpha: 0;
--st-photo-grayscale: 1;
+ /* Theme-aware badge alpha (default dark) */
+ --st-badge-alpha: 0.4;
}
/* Light theme */
@@ -75,11 +78,14 @@ body {
--st-controls-rgb: 238 238 239;
--st-controls-border: #dcdcdf; /* requested light button border */
--st-fg: #111827;
+ --st-fg-rgb: 17 24 39;
--st-divider: #d1d5db;
--st-color-scheme: light;
--st-photo-tint-rgb: 0 0 0;
--st-photo-tint-alpha: 0;
--st-photo-grayscale: 1;
+ /* lighter badge background for light themes */
+ --st-badge-alpha: 0.1;
}
/* Gray (current) */
@@ -92,12 +98,14 @@ body {
--st-controls-rgb: 62 62 65;
--st-controls-border: #464646;
--st-fg: #ffffff;
+ --st-fg-rgb: 255 255 255;
--st-divider: #424242;
--st-color-scheme: dark;
--st-gallery-vignette-alpha: 0.3;
--st-photo-tint-rgb: 0 0 0;
--st-photo-tint-alpha: 0;
--st-photo-grayscale: 1;
+ --st-badge-alpha: 0.4;
}
/* Full black */
@@ -110,11 +118,13 @@ body {
--st-controls-rgb: 17 17 17;
--st-controls-border: #242424;
--st-fg: #ffffff;
+ --st-fg-rgb: 255 255 255;
--st-divider: #303030;
--st-color-scheme: dark;
--st-photo-tint-rgb: 0 0 0;
--st-photo-tint-alpha: 0;
--st-photo-grayscale: 1;
+ --st-badge-alpha: 0.4;
}
/* Aurora gradient theme */
@@ -132,11 +142,13 @@ body {
--st-controls-rgb: 26 31 53;
--st-controls-border: #1e2237;
--st-fg: #f8fafc;
+ --st-fg-rgb: 248 250 252;
--st-divider: #2a3152;
--st-color-scheme: dark;
--st-photo-tint-rgb: 22 32 56; /* slightly darker bluish tint */
--st-photo-tint-alpha: 0.34;
--st-photo-grayscale: 0;
+ --st-badge-alpha: 0.4;
}
/* Sunset gradient theme: purple to orange */
@@ -153,11 +165,13 @@ body {
--st-controls-rgb: 39 25 39;
--st-controls-border: #3a253a;
--st-fg: #f8fafc;
+ --st-fg-rgb: 248 250 252;
--st-divider: #4a2e3a;
--st-color-scheme: dark;
--st-photo-tint-rgb: 96 32 48; /* warmer tint for images */
--st-photo-tint-alpha: 0.3;
--st-photo-grayscale: 0;
+ --st-badge-alpha: 0.4;
}
/* Fallback classes hooking common UI areas to variables without refactors */
@@ -179,8 +193,11 @@ body {
.bg-ui-controls {
background: var(--st-controls);
}
+.bg-ui-badge {
+ background-color: rgba(0, 0, 0, var(--st-badge-alpha));
+}
.text-base-fg {
- color: var(--st-fg);
+ color: rgb(var(--st-fg-rgb) / 1);
}
.border-ui-divider {
border-color: var(--st-divider);
diff --git a/frontend/apps/editor3d/tailwind.config.ts b/frontend/apps/editor3d/tailwind.config.ts
index d0e6a58748..01948f10e0 100644
--- a/frontend/apps/editor3d/tailwind.config.ts
+++ b/frontend/apps/editor3d/tailwind.config.ts
@@ -212,6 +212,7 @@ export default {
"controls-button": "rgb(var(--st-controls-rgb) /
)",
divider: "var(--st-divider)",
},
+ "base-fg": "rgb(var(--st-fg-rgb) / )",
media: {
"audio-tts": "#6AAA88",
"audio-v2v": "#6691B9",
diff --git a/frontend/libs/components/model-selector/src/lib/classy-model-selector.tsx b/frontend/libs/components/model-selector/src/lib/classy-model-selector.tsx
index e4f6425989..2d4c23a986 100644
--- a/frontend/libs/components/model-selector/src/lib/classy-model-selector.tsx
+++ b/frontend/libs/components/model-selector/src/lib/classy-model-selector.tsx
@@ -1,7 +1,16 @@
import { PopoverMenu, type PopoverItem } from "@storyteller/ui-popover";
-import { useClassyModelSelectorStore } from "./classy-model-selector-store";
+import {
+ useClassyModelSelectorStore,
+ useSelectedProviderForModel,
+} from "./classy-model-selector-store";
import { useEffect, useMemo } from "react";
import { ModelPage } from "./model-pages";
+import { Provider } from "@storyteller/tauri-api";
+import { getProviderDisplayName, getProviderIcon } from "./provider-icons";
+import { Model } from "@storyteller/model-list";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faCircleCheck, faChevronUp } from "@fortawesome/pro-solid-svg-icons";
+import { PROVIDER_LOOKUP_BY_PAGE } from "./provider-lookup";
interface ClassyModelSelectorProps {
items: Omit[];
@@ -12,15 +21,98 @@ interface ClassyModelSelectorProps {
panelClassName?: string;
showIconsInList?: boolean;
triggerLabel?: string;
+ providersByModel?: Partial>;
+ providerTooltipDelayMs?: number;
+}
+
+const DEFAULT_PROVIDER_OPTIONS: Provider[] = [
+ Provider.ArtCraft,
+ Provider.Fal,
+ Provider.Sora,
+];
+
+function ProviderTooltipContent({
+ page,
+ modelId,
+ model,
+ modelLabel,
+ allowedProviders,
+ onFinished,
+}: {
+ page: ModelPage;
+ modelId?: string;
+ model?: Model;
+ modelLabel?: string;
+ allowedProviders: Provider[];
+ onFinished?: () => void;
+}) {
+ const { setSelectedModel, setSelectedProvider } =
+ useClassyModelSelectorStore();
+ const selectedProvider = useSelectedProviderForModel(page, modelId);
+
+ // Initialize provider for this model if missing
+ useEffect(() => {
+ if (!modelId) return;
+ if (!selectedProvider && allowedProviders.length > 0) {
+ setSelectedProvider(page, modelId, allowedProviders[0]);
+ }
+ }, [page, modelId, selectedProvider, allowedProviders, setSelectedProvider]);
+
+ if (!modelId) return null as any;
+
+ return (
+
+
+ Select Provider
+
+
+ {allowedProviders.map((p) => (
+
+ ))}
+
+
+ );
}
export function ClassyModelSelector({
items,
page,
+ providersByModel,
+ providerTooltipDelayMs = 300,
...popoverProps
}: ClassyModelSelectorProps) {
- const { selectedModels, setSelectedModel } = useClassyModelSelectorStore();
+ const { selectedModels, setSelectedModel, setSelectedProvider } =
+ useClassyModelSelectorStore();
const selectedModel = selectedModels[page] || items[0]?.model;
+ const selectedProvider = useSelectedProviderForModel(page, selectedModel?.id);
+ const selectedProvidersByModel = useClassyModelSelectorStore(
+ (s) => s.selectedProviders[page] ?? {}
+ );
// For the first mount, make sure the selected model is set for other components to listen
useEffect(() => {
@@ -30,6 +122,23 @@ export function ClassyModelSelector({
}
}, []);
+ // Initialize a default provider for each model so we can render icons even when not selected
+ useEffect(() => {
+ for (const item of items) {
+ const modelId = item.model?.id;
+ if (!modelId) continue;
+ if (selectedProvidersByModel[modelId]) continue;
+ const allowed =
+ providersByModel?.[modelId] ??
+ PROVIDER_LOOKUP_BY_PAGE[page]?.[modelId] ??
+ DEFAULT_PROVIDER_OPTIONS;
+ if (allowed.length > 0) {
+ setSelectedProvider(page, modelId, allowed[0]);
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [items, providersByModel, page, selectedProvidersByModel]);
+
const handleModelSelect = (item: PopoverItem) => {
console.log(`Model selector changed on page "${page}": `, item.model);
setSelectedModel(page, item.model!);
@@ -37,21 +146,108 @@ export function ClassyModelSelector({
const modelList = useMemo(
() =>
- items.map((item) => ({
- ...item,
- selected: item.model === selectedModel,
- })),
- [items, selectedModel]
+ items.map((item) => {
+ const modelId = item.model?.id;
+ const allowedProviders: Provider[] = modelId
+ ? providersByModel?.[modelId] ??
+ PROVIDER_LOOKUP_BY_PAGE[page]?.[modelId] ??
+ DEFAULT_PROVIDER_OPTIONS
+ : DEFAULT_PROVIDER_OPTIONS;
+
+ return {
+ ...item,
+ selected: item.model === selectedModel,
+ hoverTooltip: (close: () => void) => (
+
+ ),
+ tooltipDelayMs: providerTooltipDelayMs,
+ trailing:
+ item.model !== selectedModel
+ ? (() => {
+ const prov = modelId
+ ? selectedProvidersByModel[modelId]
+ : undefined;
+ const iconProvider = prov ?? allowedProviders[0];
+ return iconProvider ? (
+
+
+ {getProviderIcon(iconProvider)}
+
+
+ ) : undefined;
+ })()
+ : undefined,
+ selectedRight:
+ item.model === selectedModel && selectedProvider ? (
+
+
+ {getProviderIcon(selectedProvider)}
+
+
+ ) : undefined,
+ } as PopoverItem;
+ }),
+ [
+ items,
+ selectedModel,
+ selectedProvider,
+ selectedProvidersByModel,
+ page,
+ providersByModel,
+ providerTooltipDelayMs,
+ ]
);
return (
-
+
+
Model
+
{
+ const modelTitle = selectedItem?.label ?? "";
+ const providerIcon = selectedProvider
+ ? getProviderIcon(selectedProvider)
+ : null;
+ return (
+
+
+
+
+ {modelTitle}
+
+
+
+ via
+ {providerIcon && (
+ {providerIcon}
+ )}
+
+ {selectedProvider
+ ? getProviderDisplayName(selectedProvider)
+ : ""}
+
+
+
+
+
+ );
+ }}
+ />
+
);
}
diff --git a/frontend/libs/components/model-selector/src/lib/provider-selector.tsx b/frontend/libs/components/model-selector/src/lib/provider-selector.tsx
index a5149e60aa..44f5e68827 100644
--- a/frontend/libs/components/model-selector/src/lib/provider-selector.tsx
+++ b/frontend/libs/components/model-selector/src/lib/provider-selector.tsx
@@ -1,3 +1,7 @@
+///////////////////////////////////////////
+// DEPRECATED: Use ClassyModelSelector only
+///////////////////////////////////////////
+
import { useEffect, useMemo } from "react";
import { PopoverMenu, type PopoverItem } from "@storyteller/ui-popover";
import { ModelPage } from "./model-pages";
diff --git a/frontend/libs/components/popover/src/lib/popover.tsx b/frontend/libs/components/popover/src/lib/popover.tsx
index dede15d6f7..5ff0872129 100644
--- a/frontend/libs/components/popover/src/lib/popover.tsx
+++ b/frontend/libs/components/popover/src/lib/popover.tsx
@@ -1,4 +1,4 @@
-import { ReactNode, useRef, useEffect } from "react";
+import { ReactNode, useRef, useEffect, useState } from "react";
import {
Popover,
Transition,
@@ -14,6 +14,7 @@ import {
faCircleCheck,
} from "@fortawesome/pro-solid-svg-icons";
import { Model, ModelInfo } from "@storyteller/model-list";
+import { Tooltip } from "@storyteller/ui-tooltip";
// Global hover manager to debounce close across adjacent hover popovers
let globalCloseTimer: NodeJS.Timeout | null = null;
@@ -35,6 +36,8 @@ const scheduleGlobalClose = (fn: () => void, delayMs: number) => {
const ST_POPOVER_OPEN_EVENT = "st-popover-open";
let popoverIdCounter = 0;
+type HoverTooltipContent = ReactNode | ((close: () => void) => ReactNode);
+
export interface PopoverItem {
label: string;
selected: boolean;
@@ -49,6 +52,14 @@ export interface PopoverItem {
}>;
modelInfo?: ModelInfo;
model?: Model; // NB: Let's migrate to using this.
+ // Optional trailing content rendered on the far right of each list row
+ // (e.g., a settings button or tooltip trigger)
+ trailing?: ReactNode;
+ // Optional tooltip content to show when hovering the entire row
+ hoverTooltip?: HoverTooltipContent;
+ tooltipDelayMs?: number;
+ // Optional custom right-side node shown when item is selected
+ selectedRight?: ReactNode;
}
interface PopoverMenuProps {
@@ -71,6 +82,7 @@ interface PopoverMenuProps {
panelActionLabel?: string;
onOpenChange?: (open: boolean) => void;
closeOnUnhover?: boolean;
+ renderTrigger?: (selected?: PopoverItem, open?: boolean) => ReactNode;
}
export const PopoverMenu = ({
@@ -93,8 +105,10 @@ export const PopoverMenu = ({
panelActionLabel,
onOpenChange,
closeOnUnhover = false,
+ renderTrigger,
}: PopoverMenuProps) => {
const selectedItem = items.find((item) => item.selected);
+ const [openTooltipIdx, setOpenTooltipIdx] = useState(null);
const handleItemClick = (item: PopoverItem, close: () => void) => {
if (mode === "button" && item.action && onPanelAction) {
@@ -260,22 +274,31 @@ export const PopoverMenu = ({
}}
ref={popoverButtonRef}
>
- {triggerIcon}
- {mode === "toggle" && selectedItem ? (
- {selectedItem.label}
- ) : null}
- {mode === "default" && triggerLabel ? (
- {triggerLabel}
- ) : null}
- {mode === "hoverSelect" && selectedItem ? (
-
-
{triggerLabel}
-
+ {renderTrigger ? (
+ renderTrigger(selectedItem, open)
+ ) : (
+ <>
+ {triggerIcon}
+ {mode === "toggle" && selectedItem ? (
{selectedItem.label}
-
-
-
- ) : null}
+ ) : null}
+ {mode === "default" && triggerLabel ? (
+ {triggerLabel}
+ ) : null}
+ {mode === "hoverSelect" && selectedItem ? (
+
+
{triggerLabel}
+
+ {selectedItem.label}
+
+
+
+ ) : null}
+ >
+ )}
{items.map((item, index) => (
-
{
- if (!item.disabled) {
- handleItemClick(item, close);
+ {item.hoverTooltip ? (
+
void
+ ) => ReactNode
+ )(close)
+ : item.hoverTooltip
}
- }}
- className={twMerge(
- "group flex cursor-pointer items-start gap-2 rounded-lg px-2 py-2 transition-all",
- item.selected
- ? "bg-ui-controls/70 border-l-4 border-primary"
- : "hover:bg-ui-controls/50",
- item.disabled
- ? "!cursor-not-allowed opacity-50"
- : ""
- )}
- style={{ minHeight: 48 }}
- >
-
-
- {showIconsInList && (
-
- {item.icon}
-
+ position="right"
+ delay={item.tooltipDelayMs ?? 300}
+ interactive
+ className="!pointer-events-auto z-50 min-w-48 rounded-lg bg-ui-panel p-1.5 shadow-lg border border-ui-panel-border"
+ onOpenChange={(open) =>
+ setOpenTooltipIdx((prev) =>
+ open ? index : prev === index ? null : prev
+ )
+ }
+ >
+
{
+ if (!item.disabled) {
+ handleItemClick(item, close);
+ }
+ }}
+ className={twMerge(
+ "group flex cursor-pointer items-start gap-2 rounded-lg px-2 py-2 transition-all",
+ item.selected
+ ? "bg-ui-controls/70 border-l-4 border-primary"
+ : "hover:bg-ui-controls/50",
+ !item.selected && openTooltipIdx === index
+ ? "bg-ui-controls/50"
+ : "",
+ item.disabled
+ ? "!cursor-not-allowed opacity-50"
+ : ""
)}
-
-
-
- {item.label}
-
+ style={{ minHeight: 48 }}
+ >
+
+
+ {showIconsInList && (
+
+ {item.icon}
+
+ )}
+
+
+
+ {item.label}
+
+
+
+ {item.description && (
+
+ {item.description}
+
+ )}
+
+
+ {item.badges &&
+ Array.isArray(item.badges) &&
+ item.badges.map((badge, i) => (
+
+
+ {badge?.icon && (
+ {badge.icon}
+ )}
+ {badge?.label || ""}
+
+
+ ))}
+
+
- {item.description && (
-
- {item.description}
+ {/* Optional trailing content (e.g., tooltip trigger) */}
+ {item.trailing && (
+
+ {item.trailing}
)}
-
- {item.badges &&
- Array.isArray(item.badges) &&
- item.badges.map((badge, i) => (
-
-
- {badge?.icon && (
- {badge.icon}
- )}
- {badge?.label || ""}
-
-
- ))}
-
+ {item.selected &&
+ (item.selectedRight ?? (
+
+
+
+ ))}
-
- {item.selected && (
-
-
-
+
+ ) : (
+
{
+ if (!item.disabled) {
+ handleItemClick(item, close);
+ }
+ }}
+ className={twMerge(
+ "group flex cursor-pointer items-start gap-2 rounded-lg px-2 py-2 transition-all",
+ item.selected
+ ? "bg-ui-controls/70 border-l-4 border-primary"
+ : "hover:bg-ui-controls/50",
+ item.disabled
+ ? "!cursor-not-allowed opacity-50"
+ : ""
)}
+ style={{ minHeight: 48 }}
+ >
+
+
+ {showIconsInList && (
+
+ {item.icon}
+
+ )}
+
+
+
+ {item.label}
+
+
+
+ {item.description && (
+
+ {item.description}
+
+ )}
+
+
+ {item.badges &&
+ Array.isArray(item.badges) &&
+ item.badges.map((badge, i) => (
+
+
+ {badge?.icon && (
+ {badge.icon}
+ )}
+ {badge?.label || ""}
+
+
+ ))}
+
+
+
+
+ {/* Optional trailing content (e.g., tooltip trigger) */}
+ {item.trailing && (
+
+ {item.trailing}
+
+ )}
+
+ {item.selected && (
+
+
+
+ )}
+
-
+ )}
{item.divider && (
)}
@@ -428,59 +559,164 @@ export const PopoverMenu = ({
{items.map((item, index) => (
-
+
+ )}
{item.divider && (
)}
diff --git a/frontend/libs/components/tooltip/src/lib/tooltip.tsx b/frontend/libs/components/tooltip/src/lib/tooltip.tsx
index 039e8d9c10..abee50dc15 100644
--- a/frontend/libs/components/tooltip/src/lib/tooltip.tsx
+++ b/frontend/libs/components/tooltip/src/lib/tooltip.tsx
@@ -16,6 +16,7 @@ interface TooltipProps {
* immediately when the cursor leaves the trigger. Useful for menus.
*/
interactive?: boolean;
+ onOpenChange?: (open: boolean) => void;
}
export const Tooltip = ({
@@ -28,6 +29,7 @@ export const Tooltip = ({
imageSrc,
description,
interactive = false,
+ onOpenChange,
}: TooltipProps) => {
const [isShowing, setIsShowing] = useState(false);
const triggerRef = useRef
(null);
@@ -122,6 +124,10 @@ export const Tooltip = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isHoveringTrigger, isHoveringTooltip, interactive]);
+ useEffect(() => {
+ onOpenChange?.(isShowing);
+ }, [isShowing, onOpenChange]);
+
return (