Polish Meshy-backed studio results

Co-authored-by: Luke The Dev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-04-19 02:30:27 +00:00
co-authored by Luke The Dev
parent fa2e15edb8
commit dcfc93c285
2 changed files with 160 additions and 4 deletions
@@ -112,7 +112,7 @@ const SceneContents = ({ sceneDraft }: { sceneDraft: StudioWorldDraft }) => {
type StudioWorldPreviewProps = {
sceneDraft: StudioWorldDraft;
referenceImage?: StudioSourceImageRecord | null;
project?: Pick<StudioProjectRecord, "mode"> | null;
project?: Pick<StudioProjectRecord, "mode" | "provider" | "externalModel"> | null;
};
export function StudioWorldPreview({
@@ -120,6 +120,23 @@ export function StudioWorldPreview({
referenceImage = null,
project = null,
}: StudioWorldPreviewProps) {
const isRemoteAiProject = project?.provider === "meshy";
const remoteReady = Boolean(project?.externalModel?.glbUrl);
const remoteThumbnailUrl = project?.externalModel?.thumbnailUrl ?? null;
const previewLabel = isRemoteAiProject
? remoteReady
? "Remote AI result available"
: "Remote AI task in progress"
: "Local Studio preview";
const previewSubLabel = isRemoteAiProject
? remoteReady
? "Showing local fallback scene while provider GLB and thumbnail are ready."
: "Showing local fallback scene until the provider finishes."
: project?.mode === "image_avatar"
? "Image-guided avatar proxy."
: project?.mode === "image_mesh"
? "Image-guided mesh draft."
: "Local world draft.";
return (
<div className="relative h-full min-h-[360px] w-full overflow-hidden rounded-2xl border border-border/60 bg-black/70">
<Canvas
@@ -137,11 +154,17 @@ export function StudioWorldPreview({
Claw3D Studio Preview
</p>
<p className="mt-1 text-sm text-white/90">{sceneDraft.promptSummary}</p>
<p className="mt-1 text-[11px] text-white/65">{previewLabel}</p>
</div>
<div className="rounded-full border border-white/15 bg-black/35 px-3 py-1 font-mono text-[10px] uppercase tracking-[0.16em] text-white/75">
{sceneDraft.assets.length} assets
</div>
</div>
<div className="pointer-events-none absolute inset-x-0 top-16 px-4">
<div className="inline-flex rounded-full border border-white/10 bg-black/35 px-3 py-1 font-mono text-[10px] uppercase tracking-[0.14em] text-white/70">
{previewSubLabel}
</div>
</div>
{referenceImage ? (
<div className="pointer-events-none absolute bottom-4 left-4 w-36 overflow-hidden rounded-2xl border border-white/15 bg-black/45 shadow-2xl backdrop-blur">
<Image
@@ -158,7 +181,31 @@ export function StudioWorldPreview({
</div>
<div className="mt-1 truncate text-xs text-white/85">{referenceImage.fileName}</div>
<div className="mt-1 text-[11px] text-white/60">
{project?.mode === "image_avatar" ? "Image-guided avatar proxy." : "Reference image attached."}
{project?.mode === "image_avatar"
? "Image-guided avatar proxy."
: project?.mode === "image_mesh"
? "Image-guided mesh draft."
: "Reference image attached."}
</div>
</div>
</div>
) : null}
{remoteThumbnailUrl ? (
<div className="pointer-events-none absolute bottom-4 right-4 w-40 overflow-hidden rounded-2xl border border-white/15 bg-black/45 shadow-2xl backdrop-blur">
<Image
src={remoteThumbnailUrl}
alt="Remote AI thumbnail"
width={160}
height={160}
className="h-28 w-full object-cover"
unoptimized
/>
<div className="px-3 py-2">
<div className="font-mono text-[10px] uppercase tracking-[0.16em] text-cyan-100/80">
Provider result
</div>
<div className="mt-1 text-[11px] text-white/60">
Remote thumbnail returned by the AI provider.
</div>
</div>
</div>
@@ -63,6 +63,23 @@ type StudioWorldResponse = {
error?: string;
};
const downloadFileFromUrl = async (params: {
url: string;
filename: string;
}) => {
const response = await fetch(params.url);
if (!response.ok) {
throw new Error("Failed to download provider GLB.");
}
const blob = await response.blob();
const objectUrl = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = objectUrl;
link.download = params.filename;
link.click();
URL.revokeObjectURL(objectUrl);
};
const formatTimestamp = (value: string) => {
const parsed = Date.parse(value);
if (!Number.isFinite(parsed)) return value;
@@ -262,6 +279,38 @@ export function StudioWorldScreen() {
}
};
const handleSyncProject = async (projectId: string) => {
setBusy(true);
setStatusLine("Syncing provider task status.");
try {
const response = await fetch(
`/api/studio-world?action=task-status&projectId=${encodeURIComponent(projectId)}`,
{ cache: "no-store" },
);
const body = (await response.json()) as StudioWorldResponse;
if (!response.ok || !body.project) {
throw new Error(body.error || "Failed to sync provider task.");
}
setProjects((current) =>
current.map((entry) => (entry.id === body.project!.id ? body.project! : entry)),
);
setSelectedProjectId(body.project.id);
setError(null);
if (body.providerTask?.status === "SUCCEEDED") {
setStatusLine("Provider task synced. Remote GLB is ready.");
} else if (body.providerTask?.status === "FAILED" || body.providerTask?.status === "CANCELED") {
setStatusLine(body.providerTask.taskErrorMessage || "Provider task failed.");
} else {
setStatusLine(`Provider task is ${body.providerTask?.status?.toLowerCase() ?? "in progress"}.`);
}
} catch (syncError) {
setError(syncError instanceof Error ? syncError.message : "Failed to sync provider task.");
setStatusLine(null);
} finally {
setBusy(false);
}
};
const handleDelete = async (projectId: string) => {
setBusy(true);
setStatusLine("Deleting studio draft.");
@@ -324,6 +373,29 @@ export function StudioWorldScreen() {
}
};
const handleExportProviderGlb = async (project: StudioProjectRecord) => {
const glbUrl = project.externalModel?.glbUrl?.trim() ?? "";
if (!glbUrl) {
setError("Provider GLB is not ready yet.");
return;
}
setBusy(true);
setStatusLine("Downloading provider GLB.");
try {
await downloadFileFromUrl({
url: glbUrl,
filename: `${project.name.trim().replace(/\s+/g, "-").toLowerCase() || "studio-provider"}.glb`,
});
setError(null);
setStatusLine("Provider GLB downloaded.");
} catch (downloadError) {
setError(downloadError instanceof Error ? downloadError.message : "Failed to download provider GLB.");
setStatusLine(null);
} finally {
setBusy(false);
}
};
const handleApplyToOffice = async (projectId: string) => {
setBusy(true);
setStatusLine("Publishing generated layout to Claw3D office.");
@@ -596,7 +668,11 @@ export function StudioWorldScreen() {
<section className="ui-panel ui-depth-workspace min-h-0 overflow-hidden p-3">
{selectedProject ? (
<div className="flex h-full min-h-0 flex-col gap-3">
<StudioWorldPreview sceneDraft={selectedProject.sceneDraft} />
<StudioWorldPreview
sceneDraft={selectedProject.sceneDraft}
referenceImage={selectedProject.sourceImages[0] ?? null}
project={selectedProject}
/>
<div className="grid gap-3 lg:grid-cols-[minmax(0,1fr)_280px]">
<div className="ui-card max-h-52 overflow-auto p-4">
<div className="font-mono text-[10px] uppercase tracking-[0.16em] text-muted-foreground">
@@ -616,9 +692,10 @@ export function StudioWorldScreen() {
</div>
<div className="mt-3 space-y-2 text-sm text-muted-foreground">
<div className="rounded-lg border border-border/50 bg-surface-1/50 px-3 py-2">Direct GLB download.</div>
<div className="rounded-lg border border-border/50 bg-surface-1/50 px-3 py-2">Provider GLB download when remote AI finishes.</div>
<div className="rounded-lg border border-border/50 bg-surface-1/50 px-3 py-2">GLB manifest download.</div>
<div className="rounded-lg border border-border/50 bg-surface-1/50 px-3 py-2">Publish to Claw3D office layout.</div>
<div className="rounded-lg border border-border/50 bg-surface-1/50 px-3 py-2">Future provider seam for remote model workers.</div>
<div className="rounded-lg border border-border/50 bg-surface-1/50 px-3 py-2">Manual provider task sync for remote AI jobs.</div>
</div>
</div>
</div>
@@ -704,6 +781,18 @@ export function StudioWorldScreen() {
{project.externalModel.errorMessage ? ` ${project.externalModel.errorMessage}` : ""}
</div>
) : null}
{project.externalModel?.thumbnailUrl ? (
<div className="mt-3 overflow-hidden rounded-xl border border-border/60 bg-surface-1/35">
<Image
src={project.externalModel.thumbnailUrl}
alt={`${project.name} provider thumbnail`}
width={320}
height={180}
className="h-32 w-full object-cover"
unoptimized
/>
</div>
) : null}
{project.sourceImages[0] ? (
<div className="mt-3 flex items-center gap-3 rounded-xl border border-border/60 bg-surface-1/35 p-2">
<Image
@@ -723,6 +812,16 @@ export function StudioWorldScreen() {
</div>
) : null}
<div className="mt-3 flex flex-wrap gap-2">
{project.externalModel?.taskId ? (
<button
type="button"
className="ui-btn-secondary px-3 py-1.5 text-xs"
onClick={() => void handleSyncProject(project.id)}
disabled={busy}
>
Sync now
</button>
) : null}
<button
type="button"
className="ui-btn-secondary px-3 py-1.5 text-xs"
@@ -731,6 +830,16 @@ export function StudioWorldScreen() {
>
Export GLB
</button>
{project.externalModel?.glbUrl ? (
<button
type="button"
className="ui-btn-secondary px-3 py-1.5 text-xs"
onClick={() => void handleExportProviderGlb(project)}
disabled={busy}
>
Download provider GLB
</button>
) : null}
<button
type="button"
className="ui-btn-secondary px-3 py-1.5 text-xs"