mirror of
https://github.com/f/awesome-chatgpt-prompts.git
synced 2026-02-12 15:52:47 +00:00
feat(app): add MCP feature to user profile page
This commit is contained in:
@@ -62,6 +62,8 @@ export default defineConfig({
|
||||
aiSearch: true,
|
||||
// Enable AI-powered generation features (requires OPENAI_API_KEY)
|
||||
aiGeneration: true,
|
||||
// Enable MCP (Model Context Protocol) features including API key generation
|
||||
mcp: true,
|
||||
},
|
||||
|
||||
// Homepage customization
|
||||
|
||||
8
public/context7.svg
Normal file
8
public/context7.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="28" height="28" rx="4" fill="black" style="fill:black;fill-opacity:1;"/>
|
||||
<path d="M10.5724 15.2565C10.5724 17.5025 9.6613 19.3778 8.17805 21.1047H11.6319L11.6319 22.7786H6.33459V21.1895C7.95557 19.3566 8.58065 17.8628 8.58065 15.2565L10.5724 15.2565Z" fill="white" style="fill:white;fill-opacity:1;"/>
|
||||
<path d="M17.4276 15.2565C17.4276 17.5025 18.3387 19.3778 19.822 21.1047H16.3681V22.7786H21.6654V21.1895C20.0444 19.3566 19.4194 17.8628 19.4194 15.2565H17.4276Z" fill="white" style="fill:white;fill-opacity:1;"/>
|
||||
<path d="M10.5724 12.7435C10.5724 10.4975 9.66131 8.62224 8.17807 6.89532L11.6319 6.89532V5.22137L6.33461 5.22137V6.81056C7.95558 8.64343 8.58066 10.1373 8.58066 12.7435L10.5724 12.7435Z" fill="white" style="fill:white;fill-opacity:1;"/>
|
||||
<path d="M17.4276 12.7435C17.4276 10.4975 18.3387 8.62224 19.822 6.89532L16.3681 6.89532L16.3681 5.22138L21.6654 5.22138V6.81056C20.0445 8.64343 19.4194 10.1373 19.4194 12.7435H17.4276Z" fill="white" style="fill:white;fill-opacity:1;"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -7,6 +7,7 @@ import { getPromptUrl } from "@/lib/urls";
|
||||
import { Calendar, ArrowBigUp, FileText, Settings, GitPullRequest, Clock, Check, X, Pin, BadgeCheck, Users, ShieldCheck } from "lucide-react";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { db } from "@/lib/db";
|
||||
import config from "@/../prompts.config";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -326,7 +327,7 @@ export default async function UserProfilePage({ params, searchParams }: UserProf
|
||||
</div>
|
||||
{/* Actions - desktop only */}
|
||||
<div className="hidden md:flex items-center gap-2 shrink-0">
|
||||
<McpServerPopup initialUsers={[user.username]} />
|
||||
{config.features.mcp !== false && <McpServerPopup initialUsers={[user.username]} />}
|
||||
{isOwner && (
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<Link href="/settings">
|
||||
@@ -363,7 +364,7 @@ export default async function UserProfilePage({ params, searchParams }: UserProf
|
||||
|
||||
{/* Actions - mobile only */}
|
||||
<div className="md:hidden flex gap-2">
|
||||
<McpServerPopup initialUsers={[user.username]} />
|
||||
{config.features.mcp !== false && <McpServerPopup initialUsers={[user.username]} />}
|
||||
{isOwner && (
|
||||
<Button variant="outline" size="sm" asChild className="flex-1">
|
||||
<Link href="/settings">
|
||||
|
||||
260
src/app/brand/page.tsx
Normal file
260
src/app/brand/page.tsx
Normal file
@@ -0,0 +1,260 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { Download, Copy, Check } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useBranding } from "@/components/providers/branding-provider";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
interface AssetCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
bgClass: string;
|
||||
children: React.ReactNode;
|
||||
downloadUrl: string;
|
||||
filename: string;
|
||||
}
|
||||
|
||||
function AssetCard({ title, description, bgClass, children, downloadUrl, filename }: AssetCardProps) {
|
||||
const handleDownload = async () => {
|
||||
try {
|
||||
const response = await fetch(downloadUrl);
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
} catch (error) {
|
||||
console.error("Download failed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
<div className={`${bgClass} h-40 flex items-center justify-center p-8`}>
|
||||
{children}
|
||||
</div>
|
||||
<div className="p-4 flex items-center justify-between bg-background">
|
||||
<div>
|
||||
<p className="font-medium text-sm">{title}</p>
|
||||
<p className="text-xs text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" onClick={handleDownload}>
|
||||
<Download className="h-3 w-3 mr-1" />
|
||||
SVG
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ColorCardProps {
|
||||
color: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
function ColorCard({ color, name, description }: ColorCardProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(color);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="flex items-center justify-between p-3 rounded-md transition-colors hover:opacity-80"
|
||||
style={{ backgroundColor: color }}
|
||||
>
|
||||
<div className="text-left">
|
||||
<p className="font-mono text-sm font-medium" style={{ color: isLight(color) ? "#000" : "#fff" }}>
|
||||
{color}
|
||||
</p>
|
||||
<p className="text-xs opacity-80" style={{ color: isLight(color) ? "#000" : "#fff" }}>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
{copied ? (
|
||||
<Check className="h-4 w-4" style={{ color: isLight(color) ? "#000" : "#fff" }} />
|
||||
) : (
|
||||
<Copy className="h-4 w-4 opacity-60" style={{ color: isLight(color) ? "#000" : "#fff" }} />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function isLight(color: string): boolean {
|
||||
const hex = color.replace("#", "");
|
||||
const r = parseInt(hex.substr(0, 2), 16);
|
||||
const g = parseInt(hex.substr(2, 2), 16);
|
||||
const b = parseInt(hex.substr(4, 2), 16);
|
||||
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
return brightness > 128;
|
||||
}
|
||||
|
||||
export default function BrandAssetsPage() {
|
||||
const branding = useBranding();
|
||||
|
||||
// Redirect if using clone branding
|
||||
if (branding.useCloneBranding) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container max-w-4xl py-10">
|
||||
<h1 className="text-3xl font-bold mb-2">Brand Assets</h1>
|
||||
<p className="text-muted-foreground mb-10">
|
||||
Official logos, colors, and brand guidelines for {branding.name}. Free to use for press, partnerships, and community projects.
|
||||
</p>
|
||||
|
||||
<div className="space-y-10">
|
||||
{/* Logos Section */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-4">Logos</h2>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
{/* Logo for light backgrounds */}
|
||||
<AssetCard
|
||||
title="Logo"
|
||||
description="For light backgrounds"
|
||||
bgClass="bg-gray-100"
|
||||
downloadUrl="/logo.svg"
|
||||
filename="prompts-chat-logo.svg"
|
||||
>
|
||||
<Image
|
||||
src="/logo.svg"
|
||||
alt="prompts.chat logo"
|
||||
width={80}
|
||||
height={80}
|
||||
className="h-20 w-auto"
|
||||
/>
|
||||
</AssetCard>
|
||||
|
||||
{/* Logo for dark backgrounds */}
|
||||
<AssetCard
|
||||
title="Logo"
|
||||
description="For dark backgrounds"
|
||||
bgClass="bg-gray-900"
|
||||
downloadUrl="/logo-dark.svg"
|
||||
filename="prompts-chat-logo-dark.svg"
|
||||
>
|
||||
<Image
|
||||
src="/logo-dark.svg"
|
||||
alt="prompts.chat logo dark"
|
||||
width={80}
|
||||
height={80}
|
||||
className="h-20 w-auto"
|
||||
/>
|
||||
</AssetCard>
|
||||
|
||||
{/* Logo with text - light */}
|
||||
<AssetCard
|
||||
title="Logo with Name"
|
||||
description="For light backgrounds"
|
||||
bgClass="bg-gray-100"
|
||||
downloadUrl="/logo.svg"
|
||||
filename="prompts-chat-logo.svg"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<Image
|
||||
src="/logo.svg"
|
||||
alt="prompts.chat logo"
|
||||
width={48}
|
||||
height={48}
|
||||
className="h-12 w-auto"
|
||||
/>
|
||||
<span className="text-2xl font-bold text-gray-900">{branding.name}</span>
|
||||
</div>
|
||||
</AssetCard>
|
||||
|
||||
{/* Logo with text - dark */}
|
||||
<AssetCard
|
||||
title="Logo with Name"
|
||||
description="For dark backgrounds"
|
||||
bgClass="bg-gray-900"
|
||||
downloadUrl="/logo-dark.svg"
|
||||
filename="prompts-chat-logo-dark.svg"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<Image
|
||||
src="/logo-dark.svg"
|
||||
alt="prompts.chat logo dark"
|
||||
width={48}
|
||||
height={48}
|
||||
className="h-12 w-auto"
|
||||
/>
|
||||
<span className="text-2xl font-bold text-white">{branding.name}</span>
|
||||
</div>
|
||||
</AssetCard>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Colors Section */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-4">Brand Colors</h2>
|
||||
<p className="text-sm text-muted-foreground mb-4">Click to copy hex value</p>
|
||||
<div className="grid gap-2">
|
||||
<ColorCard
|
||||
color="#000000"
|
||||
name="Primary"
|
||||
description="Primary brand color"
|
||||
/>
|
||||
<ColorCard
|
||||
color="#ffffff"
|
||||
name="Background"
|
||||
description="Light background"
|
||||
/>
|
||||
<ColorCard
|
||||
color="#6366f1"
|
||||
name="Accent"
|
||||
description="Indigo accent color"
|
||||
/>
|
||||
<ColorCard
|
||||
color="#71717a"
|
||||
name="Muted"
|
||||
description="Muted text color"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Usage Guidelines */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-4">Usage Guidelines</h2>
|
||||
<div className="prose prose-neutral dark:prose-invert max-w-none">
|
||||
<ul className="list-disc list-inside text-muted-foreground space-y-2">
|
||||
<li>Do not stretch, distort, or rotate the logo</li>
|
||||
<li>Maintain adequate spacing around the logo</li>
|
||||
<li>Use the dark logo on light backgrounds and vice versa</li>
|
||||
<li>Do not add effects like shadows or gradients to the logo</li>
|
||||
<li>The logo should be clearly visible against the background</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* License */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-4">License</h2>
|
||||
<p className="text-muted-foreground">
|
||||
The {branding.name} brand assets are provided under{" "}
|
||||
<a
|
||||
href="https://creativecommons.org/publicdomain/zero/1.0/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground"
|
||||
>
|
||||
CC0 1.0 Universal
|
||||
</a>
|
||||
. You are free to use these assets for any purpose without attribution.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { getTranslations } from "next-intl/server";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { db } from "@/lib/db";
|
||||
import config from "@/../prompts.config";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PromptList } from "@/components/prompts/prompt-list";
|
||||
import { SubscribeButton } from "@/components/categories/subscribe-button";
|
||||
@@ -139,7 +140,7 @@ export default async function CategoryPage({ params }: CategoryPageProps) {
|
||||
<span>{category._count.subscribers} subscribers</span>
|
||||
</div>
|
||||
</div>
|
||||
<McpServerPopup initialCategories={[slug]} />
|
||||
{config.features.mcp !== false && <McpServerPopup initialCategories={[slug]} />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,10 +10,11 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { McpConfigTabs } from "@/components/mcp/mcp-config-tabs";
|
||||
import config from "@/../prompts.config";
|
||||
|
||||
export const metadata = {
|
||||
title: "API Documentation - prompts.chat",
|
||||
description: "MCP-first API for searching and discovering AI prompts programmatically",
|
||||
description: "API for searching and discovering AI prompts programmatically",
|
||||
};
|
||||
|
||||
export default async function ApiDocsPage() {
|
||||
@@ -25,83 +26,87 @@ export default async function ApiDocsPage() {
|
||||
<div className="container max-w-4xl py-10">
|
||||
<h1 className="text-2xl font-bold mb-2">API Documentation</h1>
|
||||
<p className="text-muted-foreground mb-8">
|
||||
prompts.chat provides an MCP-first API for searching and discovering AI prompts programmatically.
|
||||
Use the MCP endpoint directly with any MCP-compatible client, or make standard HTTP requests.
|
||||
{config.features.mcp !== false
|
||||
? "prompts.chat provides an MCP-first API for searching and discovering AI prompts programmatically. Use the MCP endpoint directly with any MCP-compatible client, or make standard HTTP requests."
|
||||
: "prompts.chat provides an API for searching and discovering AI prompts programmatically."
|
||||
}
|
||||
</p>
|
||||
|
||||
<div className="prose prose-neutral dark:prose-invert max-w-none space-y-10">
|
||||
{/* MCP Overview */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Zap className="h-5 w-5" />
|
||||
MCP-First API
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Our API is built on the{" "}
|
||||
<Link
|
||||
href="https://modelcontextprotocol.io"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground"
|
||||
>
|
||||
Model Context Protocol (MCP)
|
||||
</Link>
|
||||
, enabling seamless integration with AI assistants, IDEs, and automation tools.
|
||||
The same endpoint works for both MCP clients and traditional REST-style requests.
|
||||
</p>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||
<p className="text-muted-foreground"># MCP Endpoint</p>
|
||||
<p>POST {baseUrl}/api/mcp</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Using with MCP Clients */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5" />
|
||||
Using with MCP Clients
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Add prompts.chat to your MCP client configuration. Choose your client and connection type below:
|
||||
</p>
|
||||
<McpConfigTabs baseUrl={baseUrl} className="[&_button]:text-sm [&_button]:px-3 [&_button]:py-1.5 [&_pre]:text-sm [&_pre]:p-4" />
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<strong>Remote</strong> connects directly to prompts.chat API. <strong>Local</strong> runs the MCP server locally via npx.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Authentication */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Key className="h-5 w-5" />
|
||||
Authentication
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Most API features work without authentication. However, to save prompts via MCP or access your private prompts,
|
||||
you need to authenticate using an API key.
|
||||
</p>
|
||||
|
||||
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-3">
|
||||
<div>
|
||||
<p className="font-medium">Generate an API Key</p>
|
||||
{config.features.mcp !== false && (
|
||||
<>
|
||||
{/* MCP Overview */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Zap className="h-5 w-5" />
|
||||
MCP-First API
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Go to{" "}
|
||||
<Link href="/settings" className="underline hover:text-foreground">
|
||||
Settings
|
||||
Our API is built on the{" "}
|
||||
<Link
|
||||
href="https://modelcontextprotocol.io"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground"
|
||||
>
|
||||
Model Context Protocol (MCP)
|
||||
</Link>
|
||||
{" "}to generate your API key. Keys start with <code className="bg-muted px-1.5 py-0.5 rounded">pchat_</code>.
|
||||
, enabling seamless integration with AI assistants, IDEs, and automation tools.
|
||||
The same endpoint works for both MCP clients and traditional REST-style requests.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium">Using the API Key</p>
|
||||
<p className="text-muted-foreground">
|
||||
Pass your API key via the <code className="bg-muted px-1.5 py-0.5 rounded">PROMPTS_API_KEY</code> header.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||
<p className="text-muted-foreground"># MCP Endpoint</p>
|
||||
<p>POST {baseUrl}/api/mcp</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`# Remote: HTTP transport with headers
|
||||
{/* Using with MCP Clients */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5" />
|
||||
Using with MCP Clients
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Add prompts.chat to your MCP client configuration. Choose your client and connection type below:
|
||||
</p>
|
||||
<McpConfigTabs baseUrl={baseUrl} className="[&_button]:text-sm [&_button]:px-3 [&_button]:py-1.5 [&_pre]:text-sm [&_pre]:p-4" />
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<strong>Remote</strong> connects directly to prompts.chat API. <strong>Local</strong> runs the MCP server locally via npx.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Authentication */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Key className="h-5 w-5" />
|
||||
Authentication
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Most API features work without authentication. However, to save prompts via MCP or access your private prompts,
|
||||
you need to authenticate using an API key.
|
||||
</p>
|
||||
|
||||
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-3">
|
||||
<div>
|
||||
<p className="font-medium">Generate an API Key</p>
|
||||
<p className="text-muted-foreground">
|
||||
Go to{" "}
|
||||
<Link href="/settings" className="underline hover:text-foreground">
|
||||
Settings
|
||||
</Link>
|
||||
{" "}to generate your API key. Keys start with <code className="bg-muted px-1.5 py-0.5 rounded">pchat_</code>.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium">Using the API Key</p>
|
||||
<p className="text-muted-foreground">
|
||||
Pass your API key via the <code className="bg-muted px-1.5 py-0.5 rounded">PROMPTS_API_KEY</code> header.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`# Remote: HTTP transport with headers
|
||||
"prompts-chat": {
|
||||
"url": "${baseUrl}/api/mcp",
|
||||
"headers": {
|
||||
@@ -123,30 +128,30 @@ curl -X POST ${baseUrl}/api/mcp \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "PROMPTS_API_KEY: pchat_your_api_key_here" \\
|
||||
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'`}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<strong>Remote (HTTP)</strong> sends requests to prompts.chat with the API key in headers.
|
||||
<strong> Local (stdio)</strong> runs the MCP server locally via npx with the API key as an environment variable.
|
||||
With authentication, you can use the <code className="bg-muted px-1.5 py-0.5 rounded">save_prompt</code> tool
|
||||
and search results will include your private prompts.
|
||||
</p>
|
||||
</section>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<strong>Remote (HTTP)</strong> sends requests to prompts.chat with the API key in headers.
|
||||
<strong> Local (stdio)</strong> runs the MCP server locally via npx with the API key as an environment variable.
|
||||
With authentication, you can use the <code className="bg-muted px-1.5 py-0.5 rounded">save_prompt</code> tool
|
||||
and search results will include your private prompts.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* MCP Prompts */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5" />
|
||||
MCP Prompts
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
All public prompts are exposed as native MCP prompts. This allows MCP clients to list
|
||||
and use prompts directly via slash commands or prompt pickers. You can filter prompts
|
||||
by category or tag using URL query parameters.
|
||||
</p>
|
||||
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`# Filter by users (one or more usernames)
|
||||
{/* MCP Prompts */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5" />
|
||||
MCP Prompts
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
All public prompts are exposed as native MCP prompts. This allows MCP clients to list
|
||||
and use prompts directly via slash commands or prompt pickers. You can filter prompts
|
||||
by category or tag using URL query parameters.
|
||||
</p>
|
||||
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`# Filter by users (one or more usernames)
|
||||
"prompts-chat": {
|
||||
"url": "${baseUrl}/api/mcp?users=f,torvalds"
|
||||
}
|
||||
@@ -165,24 +170,24 @@ curl -X POST ${baseUrl}/api/mcp \\
|
||||
"prompts-chat": {
|
||||
"url": "${baseUrl}/api/mcp?users=f&categories=coding&tags=js"
|
||||
}`}</pre>
|
||||
</div>
|
||||
|
||||
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-3">
|
||||
<div>
|
||||
<p className="font-medium">prompts/list</p>
|
||||
<p className="text-muted-foreground">Browse all available prompts with pagination support.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium">prompts/get</p>
|
||||
<p className="text-muted-foreground">
|
||||
Retrieve a prompt by ID. Variables ({"${name}"} or {"${name:default}"}) are automatically
|
||||
substituted with provided arguments.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-3">
|
||||
<div>
|
||||
<p className="font-medium">prompts/list</p>
|
||||
<p className="text-muted-foreground">Browse all available prompts with pagination support.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium">prompts/get</p>
|
||||
<p className="text-muted-foreground">
|
||||
Retrieve a prompt by ID. Variables ({"${name}"} or {"${name:default}"}) are automatically
|
||||
substituted with provided arguments.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`# List prompts
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`# List prompts
|
||||
curl -X POST ${baseUrl}/api/mcp \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Accept: application/json, text/event-stream" \\
|
||||
@@ -201,15 +206,15 @@ curl -X POST ${baseUrl}/api/mcp \\
|
||||
"arguments": { "topic": "AI safety" }
|
||||
}
|
||||
}'`}</pre>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Available Tools */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Box className="h-5 w-5" />
|
||||
Available Tools
|
||||
</h2>
|
||||
{/* Available Tools */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Box className="h-5 w-5" />
|
||||
Available Tools
|
||||
</h2>
|
||||
|
||||
{/* search_prompts Tool */}
|
||||
<div className="space-y-4">
|
||||
@@ -408,17 +413,17 @@ curl -X POST ${baseUrl}/api/mcp \\
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* MCP Protocol Example */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Code className="h-5 w-5" />
|
||||
MCP Protocol Examples
|
||||
</h2>
|
||||
{/* MCP Protocol Example */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Code className="h-5 w-5" />
|
||||
MCP Protocol Examples
|
||||
</h2>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h3 className="font-medium">Initialize Connection</h3>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
|
||||
<div className="space-y-3">
|
||||
<h3 className="font-medium">Initialize Connection</h3>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Accept: application/json, text/event-stream" \\
|
||||
-d '{
|
||||
@@ -431,13 +436,13 @@ curl -X POST ${baseUrl}/api/mcp \\
|
||||
"clientInfo": { "name": "my-app", "version": "1.0.0" }
|
||||
}
|
||||
}'`}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h3 className="font-medium">List Available Tools</h3>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
|
||||
<div className="space-y-3">
|
||||
<h3 className="font-medium">List Available Tools</h3>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Accept: application/json, text/event-stream" \\
|
||||
-d '{
|
||||
@@ -445,13 +450,13 @@ curl -X POST ${baseUrl}/api/mcp \\
|
||||
"id": 2,
|
||||
"method": "tools/list"
|
||||
}'`}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h3 className="font-medium">Search Prompts</h3>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
|
||||
<div className="space-y-3">
|
||||
<h3 className="font-medium">Search Prompts</h3>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Accept: application/json, text/event-stream" \\
|
||||
-d '{
|
||||
@@ -466,18 +471,18 @@ curl -X POST ${baseUrl}/api/mcp \\
|
||||
}
|
||||
}
|
||||
}'`}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Response Format */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Response Format</h2>
|
||||
<p className="text-muted-foreground">
|
||||
The <code className="bg-muted px-1.5 py-0.5 rounded text-sm">search_prompts</code> tool returns results in the following format:
|
||||
</p>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`{
|
||||
{/* Response Format */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Response Format</h2>
|
||||
<p className="text-muted-foreground">
|
||||
The <code className="bg-muted px-1.5 py-0.5 rounded text-sm">search_prompts</code> tool returns results in the following format:
|
||||
</p>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`{
|
||||
"query": "code review",
|
||||
"count": 2,
|
||||
"prompts": [
|
||||
@@ -495,14 +500,16 @@ curl -X POST ${baseUrl}/api/mcp \\
|
||||
}
|
||||
]
|
||||
}`}</pre>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* REST API Alternative */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">REST API Alternative</h2>
|
||||
<h2 className="text-lg font-semibold">REST API</h2>
|
||||
<p className="text-muted-foreground">
|
||||
For simpler integrations, you can also use the standard REST endpoint:
|
||||
Use the standard REST endpoint to search and retrieve prompts:
|
||||
</p>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
<pre>{`# Search prompts via REST
|
||||
@@ -513,17 +520,6 @@ curl "${baseUrl}/api/prompts/{id}"`}</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Server Info */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Server Information</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Get MCP server information and available tools:
|
||||
</p>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
|
||||
<p>GET {baseUrl}/api/mcp</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Rate Limits */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Rate Limits</h2>
|
||||
|
||||
@@ -2,6 +2,7 @@ import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Server, Database, Key, Palette, Globe, Settings, HardDrive, Cpu, BookOpen } from "lucide-react";
|
||||
import DeepWikiIcon from "@/../public/deepwiki.svg";
|
||||
import Context7Icon from "@/../public/context7.svg";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -41,47 +42,83 @@ export default function SelfHostingPage() {
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* DeepWiki */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Image src={DeepWikiIcon} alt="" width={20} height={20} />
|
||||
DeepWiki
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
<Link
|
||||
href="https://deepwiki.com/f/awesome-chatgpt-prompts"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground"
|
||||
>
|
||||
DeepWiki
|
||||
</Link>
|
||||
{" "}provides AI-powered documentation and insights for this repository. It automatically generates comprehensive documentation from the codebase, making it easier to understand the project structure, APIs, and implementation details.
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-muted-foreground space-y-1">
|
||||
<li>AI-generated documentation from source code</li>
|
||||
<li>Interactive codebase exploration</li>
|
||||
<li>Architecture diagrams and component relationships</li>
|
||||
<li>Ask questions about the codebase</li>
|
||||
</ul>
|
||||
{/* Using Documentation AI-Agents */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-xl font-bold">Using Documentation AI-Agents</h2>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
{/* DeepWiki */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Image src={DeepWikiIcon} alt="" width={20} height={20} />
|
||||
DeepWiki
|
||||
</h3>
|
||||
<p className="text-muted-foreground">
|
||||
<Link
|
||||
href="https://deepwiki.com/f/awesome-chatgpt-prompts"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground"
|
||||
>
|
||||
DeepWiki
|
||||
</Link>
|
||||
{" "}provides AI-powered documentation and insights for this repository.
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-muted-foreground space-y-1 text-sm">
|
||||
<li>AI-generated documentation from source code</li>
|
||||
<li>Interactive codebase exploration</li>
|
||||
<li>Architecture diagrams and component relationships</li>
|
||||
<li>Available as an MCP server</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Context7 */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Image src={Context7Icon} alt="" width={20} height={20} className="rounded" />
|
||||
Context7
|
||||
</h3>
|
||||
<p className="text-muted-foreground">
|
||||
<Link
|
||||
href="https://context7.com/f/awesome-chatgpt-prompts?tab=chat"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground"
|
||||
>
|
||||
Context7
|
||||
</Link>
|
||||
{" "}is an AI-powered chat interface for exploring and understanding this repository.
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-muted-foreground space-y-1 text-sm">
|
||||
<li>Chat with the codebase using natural language</li>
|
||||
<li>Get answers with code references</li>
|
||||
<li>Understand implementation details quickly</li>
|
||||
<li>Available as an MCP server</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Prerequisites */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Database className="h-5 w-5" />
|
||||
Prerequisites
|
||||
</h2>
|
||||
<ul className="list-disc list-inside text-muted-foreground space-y-1">
|
||||
<li>Node.js 18+</li>
|
||||
<li>PostgreSQL database</li>
|
||||
<li>npm or yarn</li>
|
||||
</ul>
|
||||
</section>
|
||||
{/* Manually */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-xl font-bold">Manually</h2>
|
||||
|
||||
{/* Installation */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Quick Start</h2>
|
||||
{/* Prerequisites */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Database className="h-5 w-5" />
|
||||
Prerequisites
|
||||
</h3>
|
||||
<ul className="list-disc list-inside text-muted-foreground space-y-1">
|
||||
<li>Node.js 18+</li>
|
||||
<li>PostgreSQL database</li>
|
||||
<li>npm or yarn</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Installation */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">Quick Start</h3>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm space-y-1 overflow-x-auto">
|
||||
<p className="text-muted-foreground"># Clone the repository</p>
|
||||
<p>git clone https://github.com/f/awesome-chatgpt-prompts.git</p>
|
||||
@@ -96,14 +133,14 @@ export default function SelfHostingPage() {
|
||||
<p className="text-muted-foreground mt-3"># Start development server</p>
|
||||
<p>npm run dev</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* Environment Variables */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Key className="h-5 w-5" />
|
||||
Environment Variables
|
||||
</h2>
|
||||
{/* Environment Variables */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Key className="h-5 w-5" />
|
||||
Environment Variables
|
||||
</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Create a <code className="bg-muted px-1.5 py-0.5 rounded text-sm">.env</code> file based on <code className="bg-muted px-1.5 py-0.5 rounded text-sm">.env.example</code>:
|
||||
</p>
|
||||
@@ -280,14 +317,14 @@ export default function SelfHostingPage() {
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* Configuration */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Palette className="h-5 w-5" />
|
||||
Configuration (prompts.config.ts)
|
||||
</h2>
|
||||
{/* Configuration */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Palette className="h-5 w-5" />
|
||||
Configuration (prompts.config.ts)
|
||||
</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Customize your instance by editing <code className="bg-muted px-1.5 py-0.5 rounded text-sm">prompts.config.ts</code>:
|
||||
</p>
|
||||
@@ -508,14 +545,14 @@ export default function SelfHostingPage() {
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* White-label */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Settings className="h-5 w-5" />
|
||||
White-Label Mode
|
||||
</h2>
|
||||
{/* White-label */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Settings className="h-5 w-5" />
|
||||
White-Label Mode
|
||||
</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Set <code className="bg-muted px-1.5 py-0.5 rounded text-sm">useCloneBranding = true</code> at the top of the config to:
|
||||
</p>
|
||||
@@ -530,14 +567,14 @@ export default function SelfHostingPage() {
|
||||
<p className="text-muted-foreground">// prompts.config.ts</p>
|
||||
<p>const useCloneBranding = true;</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* Production */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Globe className="h-5 w-5" />
|
||||
Production Deployment
|
||||
</h2>
|
||||
{/* Production */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Globe className="h-5 w-5" />
|
||||
Production Deployment
|
||||
</h3>
|
||||
<div className="bg-muted rounded-lg p-4 font-mono text-sm space-y-1">
|
||||
<p>npm run build</p>
|
||||
<p>npm run start</p>
|
||||
@@ -545,11 +582,11 @@ export default function SelfHostingPage() {
|
||||
<p className="text-muted-foreground">
|
||||
Deploy to Vercel, Railway, Render, or any Node.js hosting platform. Make sure to set all environment variables in your hosting provider's dashboard.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* Support */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Support</h2>
|
||||
{/* Support */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">Support</h3>
|
||||
<p className="text-muted-foreground">
|
||||
For issues and questions, please open a{" "}
|
||||
<Link
|
||||
@@ -570,7 +607,8 @@ export default function SelfHostingPage() {
|
||||
SELF-HOSTING.md
|
||||
</Link>
|
||||
{" "}file in the repository.
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -183,7 +183,7 @@ export default async function PromptsPage({ searchParams }: PromptsPageProps) {
|
||||
{!config.homepage?.useCloneBranding && (
|
||||
<div className="flex items-center gap-2">
|
||||
<HFDataStudioDropdown aiGenerationEnabled={aiGenerationAvailable} />
|
||||
<McpServerPopup />
|
||||
{config.features.mcp !== false && <McpServerPopup />}
|
||||
</div>
|
||||
)}
|
||||
<Button size="sm" className="h-8 text-xs w-full sm:w-auto" asChild>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { redirect } from "next/navigation";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { db } from "@/lib/db";
|
||||
import config from "@/../prompts.config";
|
||||
import { ProfileForm } from "@/components/settings/profile-form";
|
||||
import { ApiKeySettings } from "@/components/settings/api-key-settings";
|
||||
|
||||
@@ -41,10 +42,12 @@ export default async function SettingsPage() {
|
||||
|
||||
<div className="space-y-6">
|
||||
<ProfileForm user={user} />
|
||||
<ApiKeySettings
|
||||
initialApiKey={user.apiKey}
|
||||
initialPublicByDefault={user.mcpPromptsPublicByDefault}
|
||||
/>
|
||||
{config.features.mcp !== false && (
|
||||
<ApiKeySettings
|
||||
initialApiKey={user.apiKey}
|
||||
initialPublicByDefault={user.mcpPromptsPublicByDefault}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getTranslations } from "next-intl/server";
|
||||
import { ArrowLeft, Tag } from "lucide-react";
|
||||
import { db } from "@/lib/db";
|
||||
import { auth } from "@/lib/auth";
|
||||
import config from "@/../prompts.config";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PromptCard } from "@/components/prompts/prompt-card";
|
||||
import { McpServerPopup } from "@/components/mcp/mcp-server-popup";
|
||||
@@ -124,7 +125,7 @@ export default async function TagPage({ params, searchParams }: TagPageProps) {
|
||||
{total} {t("prompts")}
|
||||
</span>
|
||||
</div>
|
||||
<McpServerPopup initialTags={[slug]} />
|
||||
{config.features.mcp !== false && <McpServerPopup initialTags={[slug]} />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useSession, signOut } from "next-auth/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import {
|
||||
@@ -15,6 +16,8 @@ import {
|
||||
Globe,
|
||||
Moon,
|
||||
Sun,
|
||||
Copy,
|
||||
ExternalLink,
|
||||
} from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -66,10 +69,33 @@ export function Header({ authProvider = "credentials", allowRegistration = true
|
||||
const t = useTranslations();
|
||||
const { theme, setTheme } = useTheme();
|
||||
const branding = useBranding();
|
||||
const router = useRouter();
|
||||
|
||||
const user = session?.user;
|
||||
const isAdmin = user?.role === "ADMIN";
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [logoMenuOpen, setLogoMenuOpen] = useState(false);
|
||||
const [logoMenuPosition, setLogoMenuPosition] = useState({ x: 0, y: 0 });
|
||||
|
||||
const handleLogoContextMenu = (e: React.MouseEvent) => {
|
||||
if (branding.useCloneBranding) return;
|
||||
e.preventDefault();
|
||||
setLogoMenuPosition({ x: e.clientX, y: e.clientY });
|
||||
setLogoMenuOpen(true);
|
||||
};
|
||||
|
||||
const handleCopyLogoSvg = async () => {
|
||||
try {
|
||||
const logoUrl = theme === "dark" ? (branding.logoDark || branding.logo) : branding.logo;
|
||||
if (!logoUrl) return;
|
||||
const response = await fetch(logoUrl);
|
||||
const svgContent = await response.text();
|
||||
await navigator.clipboard.writeText(svgContent);
|
||||
setLogoMenuOpen(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to copy logo:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
@@ -168,7 +194,7 @@ export function Header({ authProvider = "credentials", allowRegistration = true
|
||||
</Sheet>
|
||||
|
||||
{/* Logo */}
|
||||
<Link href="/" className="flex gap-2">
|
||||
<Link href="/" className="flex gap-2" onContextMenu={handleLogoContextMenu}>
|
||||
{branding.logo && (
|
||||
<>
|
||||
<Image
|
||||
@@ -190,6 +216,34 @@ export function Header({ authProvider = "credentials", allowRegistration = true
|
||||
<span className="font-semibold leading-none">{branding.name}</span>
|
||||
</Link>
|
||||
|
||||
{/* Logo context menu */}
|
||||
{!branding.useCloneBranding && (
|
||||
<DropdownMenu open={logoMenuOpen} onOpenChange={setLogoMenuOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<span className="sr-only">Logo menu</span>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
style={{
|
||||
position: "fixed",
|
||||
left: logoMenuPosition.x,
|
||||
top: logoMenuPosition.y,
|
||||
}}
|
||||
>
|
||||
<DropdownMenuItem onClick={handleCopyLogoSvg}>
|
||||
<Copy className="mr-2 h-4 w-4" />
|
||||
Copy Logo SVG
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => {
|
||||
router.push("/brand");
|
||||
setLogoMenuOpen(false);
|
||||
}}>
|
||||
<ExternalLink className="mr-2 h-4 w-4" />
|
||||
Brand Assets
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
|
||||
{/* Desktop nav */}
|
||||
<nav className="hidden md:flex items-center gap-1 text-sm">
|
||||
{user && (
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface FeaturesConfig {
|
||||
tags: boolean;
|
||||
aiSearch?: boolean;
|
||||
aiGeneration?: boolean;
|
||||
mcp?: boolean;
|
||||
}
|
||||
|
||||
export interface Sponsor {
|
||||
|
||||
Reference in New Issue
Block a user