feat: deep-link sidebar Layouts to specific design surface

- Sidebar Layouts items now link to /design?surface=<surfaceId>
- Design page reads ?surface= param and opens that surface directly
- DesignPage split into DesignPageInner + Suspense wrapper so
  useSearchParams works in the Next.js static build

Made-with: Cursor
This commit is contained in:
2026-03-06 14:12:29 -08:00
parent 812645cae8
commit 39167dbe45
2 changed files with 21 additions and 6 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { use, useState, useEffect } from "react";
import { use, useState, useEffect, Suspense } from "react";
import { useSearchParams } from "next/navigation";
import { toast } from "sonner";
import {
SCAFFOLD_REGISTRY, THEME_REGISTRY,
@@ -958,8 +959,9 @@ function SurfacePicker({
// Page
// ---------------------------------------------------------------------------
export default function DesignPage({ params }: { params: Promise<{ workspace: string; projectId: string }> }) {
const { projectId } = use(params);
function DesignPageInner({ projectId }: { projectId: string }) {
const searchParams = useSearchParams();
const requestedSurface = searchParams.get("surface");
const [surfaces, setSurfaces] = useState<string[]>([]);
const [surfaceThemes, setSurfaceThemes] = useState<Record<string, string>>({});
@@ -979,7 +981,11 @@ export default function DesignPage({ params }: { params: Promise<{ workspace: st
setSurfaces(loaded);
setSurfaceThemes(d.surfaceThemes ?? {});
setSelectedThemes(d.surfaceThemes ?? {});
if (loaded.length > 0) setActiveSurfaceId(loaded[0]);
// Honour ?surface= param if valid, otherwise default to first
const initial = requestedSurface && loaded.includes(requestedSurface)
? requestedSurface
: loaded[0] ?? null;
setActiveSurfaceId(initial);
return loaded;
});
@@ -1137,3 +1143,12 @@ export default function DesignPage({ params }: { params: Promise<{ workspace: st
</div>
);
}
export default function DesignPage({ params }: { params: Promise<{ workspace: string; projectId: string }> }) {
const { projectId } = use(params);
return (
<Suspense fallback={<div style={{ display: "flex", height: "100%", alignItems: "center", justifyContent: "center", color: "#a09a90", fontFamily: "Outfit, sans-serif", fontSize: "0.85rem" }}>Loading</div>}>
<DesignPageInner projectId={projectId} />
</Suspense>
);
}

View File

@@ -338,12 +338,12 @@ export function VIBNSidebar({ workspace }: VIBNSidebarProps) {
key={s}
icon={SURFACE_ICONS[s] ?? "◌"}
label={SURFACE_LABELS[s] ?? s}
href={`${base}/design`}
href={`${base}/design?surface=${encodeURIComponent(s)}`}
collapsed={collapsed}
/>
))
) : (
<SectionRow icon="◌" label="Not configured" dim collapsed={collapsed} />
<SectionRow icon="◌" label="Not configured" dim href={`${base}/design`} collapsed={collapsed} />
)}
<SectionDivider />