fix: use theme name instead of ID for v0 open title

The "Open in v0" button on /themes/[id] was using the themeId as the
title, causing v0 API to reject it. Now passes the actual theme name
and truncates the title to 32 chars to stay within v0's limit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sahaj Jain
2026-02-20 18:05:23 +05:30
parent 107b9405d0
commit dfadcd84dc
3 changed files with 10 additions and 9 deletions
+3 -2
View File
@@ -51,7 +51,8 @@ const ThemePreviewPanel = ({
styles,
currentMode,
themeId,
}: ThemeEditorPreviewProps & { themeId?: string }) => {
themeName,
}: ThemeEditorPreviewProps & { themeId?: string; themeName?: string }) => {
const { isFullscreen, toggleFullscreen } = useFullscreen();
const [activeTab, setActiveTab] = useQueryState("p", {
defaultValue: "cards",
@@ -118,7 +119,7 @@ const ThemePreviewPanel = ({
<div className="flex items-center gap-0.5">
<TooltipWrapper label="Open theme in v0" asChild>
<Button variant="ghost" onClick={() => handleOpenInV0(themeId)} className="group px-2.5">
<Button variant="ghost" onClick={() => handleOpenInV0(themeId, themeName)} className="group px-2.5">
<span className="flex items-center justify-center gap-1 transition-all group-hover:scale-110">
Open in <V0Logo className="mb-0.5 !size-5" />
</span>
+1 -1
View File
@@ -149,7 +149,7 @@ export default function ThemeView({ theme, communityData }: ThemeViewProps) {
<DialogActionsProvider>
<div className="-m-4 mt-6 flex h-[min(80svh,900px)] flex-col">
<ThemePreviewPanel styles={theme.styles} currentMode={currentMode} themeId={theme.id} />
<ThemePreviewPanel styles={theme.styles} currentMode={currentMode} themeId={theme.id} themeName={theme.name} />
</div>
<CodePanelDialog
+6 -6
View File
@@ -60,7 +60,7 @@ interface DialogActionsContextType {
handleCssImport: (css: string) => void;
handleSaveClick: (options?: { shareAfterSave?: boolean; openInV0AfterSave?: boolean }) => void;
handleShareClick: (id?: string) => Promise<void>;
handleOpenInV0: (id?: string) => void;
handleOpenInV0: (id?: string, name?: string) => void;
saveTheme: (themeName: string) => Promise<void>;
handleUpdateExisting: () => Promise<void>;
}
@@ -200,14 +200,14 @@ function useDialogActionsStore(): DialogActionsContextType {
};
// Internal helper to open v0 with a theme
const openInV0 = (id?: string) => {
const openInV0 = (id?: string, name?: string) => {
const presetId = id ?? themeState.preset;
if (!presetId) return;
const currentPreset = getPreset(presetId);
// If an explicit ID is passed but not found in presets, treat as a saved/database theme
const isSavedPreset = id ? true : !!currentPreset && currentPreset.source === "SAVED";
const themeName = currentPreset?.label || presetId;
const themeName = name || currentPreset?.label || presetId;
posthog.capture("OPEN_IN_V0", {
theme_id: presetId,
@@ -218,14 +218,14 @@ function useDialogActionsStore(): DialogActionsContextType {
const themeUrl = isSavedPreset
? `https://tweakcn.com/r/v0/${presetId}`
: `https://tweakcn.com/r/v0/${presetId}.json`;
const title = `"${themeName}" from tweakcn`;
const title = `"${themeName}" from tweakcn`.slice(0, 32);
const v0Url = `https://v0.dev/chat/api/open?url=${encodeURIComponent(themeUrl)}&title=${encodeURIComponent(title)}`;
window.open(v0Url, "_blank", "noopener,noreferrer");
};
const handleOpenInV0 = (id?: string) => {
const handleOpenInV0 = (id?: string, name?: string) => {
if (id) {
openInV0(id);
openInV0(id, name);
return;
}