diff --git a/src/app/admin/studio/studio-layout-client.tsx b/src/app/admin/studio/studio-layout-client.tsx
index ef6183cc..e9ce8fe1 100644
--- a/src/app/admin/studio/studio-layout-client.tsx
+++ b/src/app/admin/studio/studio-layout-client.tsx
@@ -7,7 +7,6 @@ import {
ClipboardCheck,
Copy,
Dumbbell,
- FolderTree,
Image as ImageIcon,
type LucideIcon,
Puzzle,
@@ -19,7 +18,7 @@ import {
import { usePathname } from "next/navigation";
import { useState } from "react";
import { CatalogManagerDialog } from "@/app/admin/catalog/catalog-manager-dialog-wrapper";
-import { OrganizeImportsDialog } from "@/components/admin/studio/organize-imports-dialog";
+import { OrganizeImportsLauncher } from "@/components/admin/studio/organize-imports-launcher";
import Link from "@/components/link";
import {
Popover,
@@ -115,7 +114,6 @@ export function StudioLayoutClient({
children: React.ReactNode;
}) {
const pathname = usePathname() ?? "";
- const [organizeOpen, setOrganizeOpen] = useState(false);
return (
-
+
>
)}
{children}
- {canEditCatalog && (
-
- )}
);
}
diff --git a/src/components/admin/studio/organize-imports-launcher.tsx b/src/components/admin/studio/organize-imports-launcher.tsx
new file mode 100644
index 00000000..9855796f
--- /dev/null
+++ b/src/components/admin/studio/organize-imports-launcher.tsx
@@ -0,0 +1,40 @@
+"use client";
+
+import { FolderTree } from "lucide-react";
+import { lazy, Suspense, useState } from "react";
+import { StudioPanelLoading } from "./studio-panel-loading";
+
+const OrganizeImportsDialog = lazy(() =>
+ import("./organize-imports-dialog").then((module) => ({
+ default: module.OrganizeImportsDialog,
+ })),
+);
+
+export function OrganizeImportsLauncher() {
+ const [open, setOpen] = useState(false);
+ const [requested, setRequested] = useState(false);
+ return (
+ <>
+
+ {requested && (
+ setOpen(false)} /> : null
+ }
+ >
+
+
+ )}
+ >
+ );
+}
diff --git a/src/lib/studio-bundle.test.ts b/src/lib/studio-bundle.test.ts
index b82c7dbc..17d02e28 100644
--- a/src/lib/studio-bundle.test.ts
+++ b/src/lib/studio-bundle.test.ts
@@ -2,8 +2,7 @@ import path from "node:path";
import { build } from "esbuild";
import { expect, it } from "vitest";
-it("keeps scale preview and generation outside the initial Nitro editor bundle", async () => {
- const entry = "src/app/admin/import/furni/nitro-editor-dialog.tsx";
+async function bundleInputs(entry: string) {
const result = await build({
entryPoints: [entry],
bundle: true,
@@ -14,6 +13,17 @@ it("keeps scale preview and generation outside the initial Nitro editor bundle",
metafile: true,
packages: "external",
alias: { "@": path.resolve("src") },
+ plugins: [
+ {
+ name: "server-action-boundary",
+ setup(builder) {
+ builder.onResolve({ filter: /^@\/actions\// }, ({ path }) => ({
+ path,
+ external: true,
+ }));
+ },
+ },
+ ],
});
const outputs = result.metafile.outputs;
const start = Object.keys(outputs).find(
@@ -34,13 +44,30 @@ it("keeps scale preview and generation outside the initial Nitro editor bundle",
const initialInputs = new Set(
[...seen].flatMap((key) => Object.keys(outputs[key].inputs)),
);
+ return { initialInputs, inputs: Object.keys(result.metafile.inputs) };
+}
+
+it("keeps scale preview and generation outside the initial Nitro editor bundle", async () => {
+ const { initialInputs, inputs } = await bundleInputs(
+ "src/app/admin/import/furni/nitro-editor-dialog.tsx",
+ );
for (const name of [
"nitro-quality-panel.tsx",
"nitro-scale32-generator.tsx",
]) {
- expect(
- Object.keys(result.metafile.inputs).some((input) => input.endsWith(name)),
- ).toBe(true);
+ expect(inputs.some((input) => input.endsWith(name))).toBe(true);
+ expect([...initialInputs].some((input) => input.endsWith(name))).toBe(
+ false,
+ );
+ }
+}, 30_000);
+
+it("loads import organization and its icon picker only when requested in Studio", async () => {
+ const { initialInputs, inputs } = await bundleInputs(
+ "src/app/admin/studio/studio-layout-client.tsx",
+ );
+ for (const name of ["organize-imports-dialog.tsx", "icon-picker.tsx"]) {
+ expect(inputs.some((input) => input.endsWith(name))).toBe(true);
expect([...initialInputs].some((input) => input.endsWith(name))).toBe(
false,
);