diff --git a/frontend/apps/artcraft-webapp/src/app/app.tsx b/frontend/apps/artcraft-webapp/src/app/app.tsx
index 4f0003483e..e45f81f926 100644
--- a/frontend/apps/artcraft-webapp/src/app/app.tsx
+++ b/frontend/apps/artcraft-webapp/src/app/app.tsx
@@ -90,15 +90,16 @@ function ProtectedContent() {
? "var(--sidebar-width)"
: "calc(var(--sidebar-width-icon) + 1.5rem)";
- // The Edit 3D and video editors host the header's actions
+ // The Edit 3D, Edit Image, and video editors host the header's actions
// (pricing/credits/task queue/profile) inside their own toolbar/header to
// reclaim vertical space, so the global header is hidden there — desktop
- // only, since the mobile route shows a gate that still needs the header's
- // nav chrome.
+ // only, since the mobile routes show the global chrome (Edit Image keeps the
+ // bar on mobile; Edit 3D shows a gate that still needs the header's nav).
const hideTopBar =
!isMobile &&
(pathname === "/edit-3d" ||
pathname.startsWith("/edit-3d/") ||
+ pathname === "/edit-image" ||
pathname === "/video-editor" ||
pathname.startsWith("/video-editor/"));
@@ -108,7 +109,14 @@ function ProtectedContent() {
style={{ "--ac-sidebar-offset": sidebarOffset } as React.CSSProperties}
>
{!hideTopBar && }
-
+ {/* The immersive editors (hideTopBar routes) paint their own #101014
+ canvas backdrop; match the inset to it so no lighter strip shows at
+ the edges. Other pages keep the standard #121212 surface. */}
+
{isMobile && }
diff --git a/frontend/apps/artcraft-webapp/src/main.tsx b/frontend/apps/artcraft-webapp/src/main.tsx
index 72e9caa1e0..c605a64d68 100644
--- a/frontend/apps/artcraft-webapp/src/main.tsx
+++ b/frontend/apps/artcraft-webapp/src/main.tsx
@@ -19,7 +19,10 @@ if (import.meta.env.DEV) {
window.location.origin,
);
// NB: This is for Brandon to test with storyteller-web locally:
- StorytellerApiHostStore.getInstance().setDevelopment();
+ // (disabled — it overrides the origin above and points at localhost:12345,
+ // which breaks dev unless a local backend is running. Re-enable only when
+ // testing against a local storyteller-web.)
+ // StorytellerApiHostStore.getInstance().setDevelopment();
} catch (e) {
console.warn("Failed to set dev API host override", e);
}
diff --git a/frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx b/frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx
index b18919e90f..10f5222d9b 100644
--- a/frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx
+++ b/frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx
@@ -148,8 +148,8 @@ export function BaseImageSelector({
onDrop={onDrop}
className={`flex h-full w-full cursor-pointer flex-col items-center justify-center gap-4 rounded-2xl border-2 border-dashed p-8 text-center transition-colors ${
isDragging
- ? "border-blue-400 bg-blue-500/10"
- : "border-ui-panel-border bg-ui-background hover:border-blue-400/50"
+ ? "border-primary-400 bg-primary/10"
+ : "border-ui-panel-border bg-ui-background hover:border-primary-400/50"
} ${busy ? "pointer-events-none opacity-60" : ""}`}
>
-
+
-
+
+ {/* With the global top bar hidden on desktop, float the same
+ credits / pricing / task-queue / profile cluster in the top-right
+ corner so those actions stay reachable while editing. */}
+ {!isMobile && (
+
+
+
+ )}
>
);
diff --git a/frontend/apps/artcraft-webapp/src/pages/pagedraw/web-adapter.tsx b/frontend/apps/artcraft-webapp/src/pages/pagedraw/web-adapter.tsx
index ae316ce501..3ecee971c6 100644
--- a/frontend/apps/artcraft-webapp/src/pages/pagedraw/web-adapter.tsx
+++ b/frontend/apps/artcraft-webapp/src/pages/pagedraw/web-adapter.tsx
@@ -21,6 +21,7 @@ import {
type BaseSelectorImage,
useSceneStore,
} from "@storyteller/ui-pagedraw";
+import { CommonAspectRatio, CommonResolution } from "@storyteller/model-list";
import {
OmniGenApi,
StorytellerApiHostStore,
@@ -141,6 +142,29 @@ const uploadBlobAsImage = async (
return mediaToken;
};
+// The PromptBox UI emits resolution/aspect-ratio as display labels ("1k",
+// "wide", …); the omni-gen REST body wants the CommonResolution /
+// CommonAspectRatio enum values ("one_k", "wide", …). Mirror the desktop
+// PageDraw adapter's mappers so an unmapped "1k" doesn't 400 the request.
+const mapAspectRatio = (ratio?: string): CommonAspectRatio | undefined => {
+ switch (ratio) {
+ case "auto": return CommonAspectRatio.Auto;
+ case "wide": return CommonAspectRatio.Wide;
+ case "tall": return CommonAspectRatio.Tall;
+ case "square": return CommonAspectRatio.Square;
+ default: return undefined;
+ }
+};
+
+const mapResolution = (res?: string): CommonResolution | undefined => {
+ switch (res) {
+ case "1k": return CommonResolution.OneK;
+ case "2k": return CommonResolution.TwoK;
+ case "4k": return CommonResolution.FourK;
+ default: return undefined;
+ }
+};
+
// PageDrawEditRequest.model is an ImageModel object (or possibly a raw id);
// the omni-gen REST body wants the string id.
const modelIdOf = (model: unknown): string => {
@@ -177,8 +201,8 @@ export const useWebPageDrawAdapter = (): PageDrawAdapter => {
model: modelIdOf(req.model),
prompt: req.prompt ?? null,
idempotency_token: crypto.randomUUID(),
- aspect_ratio: req.aspectRatio ?? null,
- resolution: req.imageResolution ?? null,
+ aspect_ratio: mapAspectRatio(req.aspectRatio) ?? null,
+ resolution: mapResolution(req.imageResolution) ?? null,
image_batch_count: req.imageCount ?? 1,
image_media_tokens: refTokens.length ? refTokens : null,
};
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 dd4c35b0c8..aa87522920 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
@@ -34,6 +34,14 @@ interface ClassyModelSelectorProps {
* provider-selection rows.
*/
variant?: "floating" | "embedded";
+ /**
+ * Whether models that support multiple providers expose the provider-picker
+ * submenu (hover panel) and the per-row provider icon chips. Defaults to
+ * true. Pages locked to a single provider (e.g. PageDraw) pass false to show
+ * only the model name — the default provider is still resolved into the
+ * store so downstream generation calls keep working.
+ */
+ showProviderSelection?: boolean;
}
const DEFAULT_PROVIDER_OPTIONS: GenerationProvider[] = [GenerationProvider.Artcraft];
@@ -113,6 +121,7 @@ export function ClassyModelSelector({
providerTooltipDelayMs = 300,
maxListHeight = "60vh",
variant = "floating",
+ showProviderSelection = true,
...popoverProps
}: ClassyModelSelectorProps) {
const { selectedModels, setSelectedModel, setSelectedProvider } =
@@ -159,7 +168,10 @@ export function ClassyModelSelector({
const modelId = item.model?.id;
const allowedProviders = item.model?.getProviders() || DEFAULT_PROVIDER_OPTIONS;
- const hasMultipleProviders = allowedProviders.length >= 2;
+ // When provider selection is hidden, treat every model as
+ // single-provider so the submenu and provider chips never render.
+ const hasMultipleProviders =
+ showProviderSelection && allowedProviders.length >= 2;
return {
...item,
@@ -213,6 +225,7 @@ export function ClassyModelSelector({
page,
providersByModel,
providerTooltipDelayMs,
+ showProviderSelection,
],
);
diff --git a/frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx b/frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx
index 856b9896f6..014ca2d00c 100644
--- a/frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx
+++ b/frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx
@@ -170,7 +170,7 @@ export const HistoryStack = ({