feat: initial commit - Jhonny Editor
- Adicionado estrutura completa do projeto - Configurado MCP server para Premiere Pro - Adicionado documentação e skills - Configurado Gitignore para o projeto
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import { AlertTriangle, ArrowDown, CheckCircle2, FileCode2, PlugZap, TerminalSquare } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import { product } from "@/lib/product"
|
||||
|
||||
const bridgeSteps = [
|
||||
{
|
||||
icon: TerminalSquare,
|
||||
title: "Your AI calls a tool",
|
||||
description: "Claude, Cursor, Windsurf, or another MCP client sends a structured request to the local server.",
|
||||
},
|
||||
{
|
||||
icon: FileCode2,
|
||||
title: "The bridge runs it in Premiere",
|
||||
description: "A versioned helper library and small ES3 script pass through a private shared temp directory.",
|
||||
},
|
||||
{
|
||||
icon: PlugZap,
|
||||
title: "Premiere returns a result",
|
||||
description: "The CEP bridge executes the command and sends structured data, confirmation, or diagnostics back to the client.",
|
||||
},
|
||||
]
|
||||
|
||||
const diagnostics = [
|
||||
{
|
||||
icon: CheckCircle2,
|
||||
title: "Automatic bridge startup",
|
||||
description: "The CEP bridge creates its temp directory and starts polling when Premiere activates.",
|
||||
},
|
||||
{
|
||||
icon: AlertTriangle,
|
||||
title: "Modal-stall diagnostics",
|
||||
description: "In-flight heartbeats help distinguish an open Premiere dialog from a disconnected bridge.",
|
||||
},
|
||||
]
|
||||
|
||||
export function ArchitectureSection() {
|
||||
return (
|
||||
<section id="how-it-works" className="reveal-section border-y border-zinc-900 bg-[#050506] px-5 py-24 md:py-32">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<figure className="mb-16 overflow-hidden rounded-xl border border-white/10 bg-black">
|
||||
<Image
|
||||
src="/marketing/premiere-pro-mcp-workflow-v1.png"
|
||||
alt="Local-first workflow: an AI assistant sends a request through the MCP bridge to Premiere, which returns a verified result."
|
||||
width={1672}
|
||||
height={941}
|
||||
sizes="(max-width: 768px) 100vw, 1152px"
|
||||
className="h-auto w-full"
|
||||
/>
|
||||
<figcaption className="border-t border-zinc-800 px-5 py-4 text-sm leading-6 text-zinc-400">
|
||||
The recommended path keeps the assistant, MCP bridge, Premiere, and project media on the same computer. A verified result still depends on a live host connection.
|
||||
</figcaption>
|
||||
</figure>
|
||||
<div className="grid gap-16 lg:grid-cols-2 lg:gap-20">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold tracking-tight text-white md:text-4xl">How the bridge <span className="text-purple-400">works</span></h2>
|
||||
<p className="mt-4 max-w-xl text-base leading-7 text-zinc-400">
|
||||
A local-first file bridge keeps the control path inspectable and avoids exposing your project media to the MCP server.
|
||||
</p>
|
||||
<div className="mt-10">
|
||||
{bridgeSteps.map((step, index) => (
|
||||
<div key={step.title} className="relative flex gap-5 pb-9 last:pb-0">
|
||||
{index < bridgeSteps.length - 1 && <span className="absolute left-[19px] top-10 h-[calc(100%-2rem)] w-px bg-zinc-800" aria-hidden="true" />}
|
||||
<span className="relative z-10 grid h-10 w-10 shrink-0 place-items-center rounded-md border border-zinc-800 bg-zinc-950 text-purple-400">
|
||||
<step.icon className="h-5 w-5" strokeWidth={1.6} />
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-zinc-100">{step.title}</h3>
|
||||
<p className="mt-2 text-sm leading-6 text-zinc-500">{step.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold tracking-tight text-white md:text-4xl">Compatibility & <span className="text-purple-400">diagnostics</span></h2>
|
||||
<p className="mt-4 max-w-xl text-base leading-7 text-zinc-400">
|
||||
The signed CEP connector is the default route for Premiere Pro {product.premiereCompatibility} on macOS and Windows. A modern UXP bridge adds capability-gated workflows on compatible newer hosts.
|
||||
</p>
|
||||
<div className="mt-10 space-y-4">
|
||||
{diagnostics.map((item) => (
|
||||
<article key={item.title} className="flex gap-4 rounded-xl border border-zinc-800 bg-zinc-950 p-5">
|
||||
<item.icon className="mt-0.5 h-6 w-6 shrink-0 text-purple-300" strokeWidth={1.5} />
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-zinc-100">{item.title}</h3>
|
||||
<p className="mt-2 text-sm leading-6 text-zinc-500">{item.description}</p>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-6 rounded-xl border border-amber-500/20 bg-amber-500/[0.04] p-5">
|
||||
<p className="text-sm leading-6 text-zinc-400">
|
||||
<span className="font-semibold text-amber-200">Know the boundary:</span> UXP is capability-gated for compatible newer Premiere workflows; it is not yet the default installer or a replacement for CEP. Premiere’s undocumented QE DOM is powerful but imperfect, so results should be verified before you rely on them.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 flex flex-col gap-4 rounded-xl border border-zinc-800 bg-black px-5 py-5 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-zinc-200">Need the full tool list, schemas, or troubleshooting guide?</p>
|
||||
<p className="mt-1 text-sm text-zinc-500">The README documents every setup path and known security boundary.</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://github.com/leancoderkavy/premiere-pro-mcp#readme"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex shrink-0 items-center gap-2 text-sm font-semibold text-purple-300 transition-colors hover:text-purple-200"
|
||||
>
|
||||
Read the documentation <ArrowDown className="h-4 w-4 -rotate-90" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import {
|
||||
ArrowUpRight,
|
||||
Check,
|
||||
CheckCircle2,
|
||||
ChevronDown,
|
||||
CircleHelp,
|
||||
Copy,
|
||||
Download,
|
||||
ExternalLink,
|
||||
Laptop,
|
||||
LockKeyhole,
|
||||
MonitorCog,
|
||||
ShieldCheck,
|
||||
Sparkles,
|
||||
} from "lucide-react"
|
||||
|
||||
import { product, safeFirstPrompt } from "@/lib/product"
|
||||
import { trackOnboardingEvent } from "@/lib/onboarding-events"
|
||||
|
||||
type AssistantRoute = {
|
||||
id: "claude" | "cursor" | "vscode" | "other"
|
||||
name: string
|
||||
shortDescription: string
|
||||
availability: string
|
||||
primaryAction: string
|
||||
href: string
|
||||
detail: string
|
||||
status: "recommended" | "guided" | "advanced"
|
||||
}
|
||||
|
||||
const assistantRoutes: AssistantRoute[] = [
|
||||
{
|
||||
id: "claude",
|
||||
name: "Claude Desktop",
|
||||
shortDescription: "Download the self-contained desktop bundle.",
|
||||
availability: "Recommended for the easiest start",
|
||||
primaryAction: "Download Claude Desktop bundle",
|
||||
href: product.downloads.claudeBundle,
|
||||
detail:
|
||||
"Open the downloaded bundle in Claude Desktop. It includes the local server, so this path does not ask you to install Node just to connect Claude.",
|
||||
status: "recommended",
|
||||
},
|
||||
{
|
||||
id: "cursor",
|
||||
name: "Cursor",
|
||||
shortDescription: "Use Cursor’s MCP settings to add the local connector.",
|
||||
availability: "Guided route",
|
||||
primaryAction: "Read the Cursor setup guide",
|
||||
href: product.links.readme,
|
||||
detail:
|
||||
"A one-click Cursor installer is not currently shipped. The guide explains the supported local route; manual configuration stays in Advanced setup below.",
|
||||
status: "guided",
|
||||
},
|
||||
{
|
||||
id: "vscode",
|
||||
name: "VS Code / Copilot",
|
||||
shortDescription: "Connect through your editor’s MCP settings.",
|
||||
availability: "Guided route",
|
||||
primaryAction: "Read the VS Code setup guide",
|
||||
href: product.links.readme,
|
||||
detail:
|
||||
"A VS Code Marketplace install is not currently shipped. Use the guide for the supported local route, or see Advanced setup if your editor asks for a server command.",
|
||||
status: "guided",
|
||||
},
|
||||
{
|
||||
id: "other",
|
||||
name: "Another assistant",
|
||||
shortDescription: "Use any MCP-compatible desktop client.",
|
||||
availability: "Advanced route",
|
||||
primaryAction: "Open compatibility guide",
|
||||
href: product.links.readme,
|
||||
detail:
|
||||
"If your assistant supports local MCP servers, it can use MCP for Adobe Premiere Pro. Its setup may require its own settings screen or the manual configuration in Advanced setup.",
|
||||
status: "advanced",
|
||||
},
|
||||
]
|
||||
|
||||
function CopyButton({ text, label }: { text: string; label: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
async function copy() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
trackOnboardingEvent("onboarding_safe_prompt_copied")
|
||||
window.setTimeout(() => setCopied(false), 1800)
|
||||
} catch {
|
||||
setCopied(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={copy}
|
||||
className="inline-flex min-h-11 shrink-0 items-center gap-2 rounded-md border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm font-medium text-zinc-200 transition-colors hover:border-zinc-500 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08080a]"
|
||||
aria-label={label}
|
||||
>
|
||||
{copied ? <Check className="h-4 w-4 text-emerald-300" aria-hidden="true" /> : <Copy className="h-4 w-4" aria-hidden="true" />}
|
||||
<span aria-live="polite">{copied ? "Copied" : "Copy prompt"}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function statusClasses(status: AssistantRoute["status"]) {
|
||||
if (status === "recommended") return "border-purple-400/50 bg-purple-500/[0.08] text-purple-100"
|
||||
if (status === "guided") return "border-zinc-700 bg-zinc-950 text-zinc-200"
|
||||
return "border-zinc-800 bg-zinc-950/50 text-zinc-300"
|
||||
}
|
||||
|
||||
export function ConnectSection() {
|
||||
const [activeId, setActiveId] = useState<AssistantRoute["id"]>("claude")
|
||||
const [advancedOpen, setAdvancedOpen] = useState(false)
|
||||
const [recoveryOpen, setRecoveryOpen] = useState(false)
|
||||
const activeRoute = assistantRoutes.find((route) => route.id === activeId) ?? assistantRoutes[0]
|
||||
|
||||
function selectRoute(route: AssistantRoute) {
|
||||
setActiveId(route.id)
|
||||
trackOnboardingEvent("onboarding_assistant_selected", { assistant: route.id })
|
||||
}
|
||||
|
||||
function trackDownload(route: AssistantRoute) {
|
||||
trackOnboardingEvent("onboarding_download_started", { route: route.id })
|
||||
}
|
||||
|
||||
return (
|
||||
<section id="install" className="reveal-section bg-black px-5 py-24 md:py-32" aria-labelledby="setup-heading">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="max-w-3xl">
|
||||
<h2 id="setup-heading" className="text-balance text-3xl font-bold tracking-tight text-white md:text-5xl">
|
||||
Connect the <span className="text-violet-300">assistant you already use.</span>
|
||||
</h2>
|
||||
<p className="mt-5 text-lg leading-8 text-zinc-400">
|
||||
Pick your client. The first step only checks that it can reach Premiere; it makes no edits.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 grid gap-3 sm:grid-cols-2 lg:grid-cols-4" role="list" aria-label="AI assistant setup routes">
|
||||
{assistantRoutes.map((route) => {
|
||||
const selected = activeId === route.id
|
||||
return (
|
||||
<button
|
||||
key={route.id}
|
||||
type="button"
|
||||
onClick={() => selectRoute(route)}
|
||||
aria-pressed={selected}
|
||||
className={`group flex min-h-52 flex-col rounded-xl border p-5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:ring-offset-4 focus-visible:ring-offset-black ${
|
||||
selected ? statusClasses(route.status) : "border-zinc-800 bg-[#08080a] text-zinc-300 hover:border-zinc-600 hover:bg-zinc-950"
|
||||
}`}
|
||||
>
|
||||
<span className="flex items-center justify-between gap-3">
|
||||
{route.status === "recommended" ? <Sparkles className="h-5 w-5 text-purple-300" aria-hidden="true" /> : <Laptop className="h-5 w-5 text-zinc-400" aria-hidden="true" />}
|
||||
<span className="text-xs font-semibold uppercase tracking-[0.14em] text-zinc-500">{route.availability}</span>
|
||||
</span>
|
||||
<span className="mt-7 text-lg font-semibold text-white">{route.name}</span>
|
||||
<span className="mt-3 text-sm leading-6 text-zinc-400">{route.shortDescription}</span>
|
||||
{selected ? <CheckCircle2 className="mt-auto h-5 w-5 text-purple-300" aria-label={`${route.name} selected`} /> : <span className="mt-auto h-5 w-5 rounded-full border border-zinc-700" aria-hidden="true" />}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid gap-6 border-y border-zinc-800 py-7 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-center">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-zinc-100">{activeRoute.name}</p>
|
||||
<p className="mt-2 max-w-3xl text-sm leading-6 text-zinc-400">{activeRoute.detail}</p>
|
||||
</div>
|
||||
<a
|
||||
href={activeRoute.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => trackDownload(activeRoute)}
|
||||
className="inline-flex min-h-12 items-center justify-center gap-2 rounded-lg bg-gradient-to-r from-[#8b7cff] to-[#ef76b9] px-5 text-sm font-semibold text-white shadow-[0_12px_40px_rgba(139,124,255,0.2)] transition-transform hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-200 focus-visible:ring-offset-4 focus-visible:ring-offset-black"
|
||||
>
|
||||
{activeRoute.id === "claude" ? <Download className="h-4 w-4" aria-hidden="true" /> : <ExternalLink className="h-4 w-4" aria-hidden="true" />}
|
||||
{activeRoute.primaryAction}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mt-14 grid gap-12 lg:grid-cols-[minmax(0,1fr)_22rem]">
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold text-white">Set up in four steps.</h3>
|
||||
<ol className="mt-8 grid gap-7 sm:grid-cols-2">
|
||||
<li className="border-t border-purple-400/35 pt-5">
|
||||
<span className="font-mono text-sm text-purple-300">01</span>
|
||||
<h4 className="mt-4 text-base font-semibold text-zinc-100">Install the Premiere connector</h4>
|
||||
<p className="mt-2 text-sm leading-6 text-zinc-400">This separate CEP connector is what lets your assistant talk to Premiere. Open the download with a trusted ZXP installer; if your computer has none, use the npm installer under Advanced setup.</p>
|
||||
<a
|
||||
href={product.downloads.signedCepConnector}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => trackOnboardingEvent("onboarding_download_started", { route: "cep_connector" })}
|
||||
className="mt-4 inline-flex min-h-11 items-center gap-2 text-sm font-semibold text-purple-200 transition-colors hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:ring-offset-2 focus-visible:ring-offset-black"
|
||||
>
|
||||
<Download className="h-4 w-4" aria-hidden="true" /> Download connector package <ArrowUpRight className="h-4 w-4" aria-hidden="true" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="border-t border-zinc-800 pt-5">
|
||||
<span className="font-mono text-sm text-purple-300">02</span>
|
||||
<h4 className="mt-4 text-base font-semibold text-zinc-100">Connect {activeRoute.name}</h4>
|
||||
<p className="mt-2 text-sm leading-6 text-zinc-400">Finish the route above, then fully close and reopen both your assistant and Premiere.</p>
|
||||
</li>
|
||||
<li className="border-t border-zinc-800 pt-5">
|
||||
<span className="font-mono text-sm text-purple-300">03</span>
|
||||
<h4 className="mt-4 text-base font-semibold text-zinc-100">Run a safe check</h4>
|
||||
<p className="mt-2 text-sm leading-6 text-zinc-400">Open a project and use the read-only prompt below. It runs the connection check and makes no changes.</p>
|
||||
</li>
|
||||
<li className="border-t border-zinc-800 pt-5">
|
||||
<span className="font-mono text-sm text-purple-300">04</span>
|
||||
<h4 className="mt-4 text-base font-semibold text-zinc-100">Ready to edit</h4>
|
||||
<p className="mt-2 text-sm leading-6 text-zinc-400">Once the connection check passes, try a preview before applying any edits.</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div className="mt-10 rounded-xl border border-purple-400/30 bg-purple-500/[0.06] p-5 sm:p-6">
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div>
|
||||
<p className="flex items-center gap-2 text-sm font-semibold text-purple-100"><ShieldCheck className="h-4 w-4" aria-hidden="true" /> Safe first prompt</p>
|
||||
<p className="mt-3 max-w-2xl font-mono text-sm leading-7 text-zinc-100">{safeFirstPrompt}</p>
|
||||
</div>
|
||||
<CopyButton text={safeFirstPrompt} label="Copy the safe first prompt" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside className="space-y-5" aria-label="Setup help and privacy">
|
||||
<div className="rounded-xl border border-zinc-800 bg-[#08080a] p-5">
|
||||
<p className="flex items-center gap-2 text-sm font-semibold text-zinc-100"><MonitorCog className="h-4 w-4 text-violet-200" aria-hidden="true" /> Compatibility details</p>
|
||||
<p className="mt-3 text-sm leading-6 text-zinc-400">The signed CEP connector is the default compatibility route for Premiere Pro {product.premiereCompatibility} on Windows and macOS.</p>
|
||||
<p className="mt-3 text-sm leading-6 text-zinc-400">The modern UXP bridge is capability-gated for compatible Premiere {product.uxpMinimumVersion}+ workflows. It is not a Creative Cloud Marketplace install and does not replace CEP in the default path.</p>
|
||||
<a href="/docs/#compatibility-heading" className="mt-4 inline-flex min-h-11 items-center gap-2 text-sm font-semibold text-purple-200 transition-colors hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08080a]">
|
||||
See compatibility details <ArrowUpRight className="h-4 w-4" aria-hidden="true" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border border-zinc-800 bg-[#08080a] p-5">
|
||||
<p className="flex items-center gap-2 text-sm font-semibold text-zinc-100"><LockKeyhole className="h-4 w-4 text-purple-300" aria-hidden="true" /> What the first check shares</p>
|
||||
<p className="mt-3 text-sm leading-6 text-zinc-400">The prompt runs a connection check; it makes no edits and does not ask for footage to be uploaded.</p>
|
||||
<p className="mt-3 text-sm leading-6 text-zinc-500">Your assistant's own privacy settings still apply. If site analytics is enabled, setup clicks record only a route and action—not prompts, media, project names, or file paths.</p>
|
||||
</div>
|
||||
|
||||
<details
|
||||
className="group rounded-xl border border-zinc-800 bg-[#08080a] p-5"
|
||||
open={recoveryOpen}
|
||||
onToggle={(event) => {
|
||||
const open = event.currentTarget.open
|
||||
setRecoveryOpen(open)
|
||||
if (open) trackOnboardingEvent("onboarding_recovery_opened")
|
||||
}}
|
||||
>
|
||||
<summary className="flex min-h-8 cursor-pointer list-none items-center justify-between gap-4 text-sm font-semibold text-zinc-100 marker:content-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300">
|
||||
<span className="flex items-center gap-2"><CircleHelp className="h-4 w-4 text-purple-300" aria-hidden="true" /> Need help connecting?</span>
|
||||
<ChevronDown className="h-4 w-4 text-zinc-400 transition-transform group-open:rotate-180" aria-hidden="true" />
|
||||
</summary>
|
||||
<ol className="mt-4 list-decimal space-y-3 pl-5 text-sm leading-6 text-zinc-400">
|
||||
<li>Quit and reopen both Premiere and your assistant.</li>
|
||||
<li>Open a project and make sure an active sequence is selected.</li>
|
||||
<li>In Premiere, look for <span className="text-zinc-200">Window → Extensions → MCP for Adobe Premiere Pro</span>.</li>
|
||||
<li>Use the safe prompt again. If it still fails, share the connection state—not your project media—with support.</li>
|
||||
</ol>
|
||||
<a href={product.links.issues} target="_blank" rel="noopener noreferrer" className="mt-5 inline-flex min-h-11 items-center gap-2 text-sm font-semibold text-purple-200 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08080a]">
|
||||
Get connection help <ArrowUpRight className="h-4 w-4" aria-hidden="true" />
|
||||
</a>
|
||||
</details>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<details
|
||||
className="group mt-14 border-t border-zinc-800 pt-6"
|
||||
open={advancedOpen}
|
||||
onToggle={(event) => {
|
||||
const open = event.currentTarget.open
|
||||
setAdvancedOpen(open)
|
||||
if (open) trackOnboardingEvent("onboarding_advanced_opened")
|
||||
}}
|
||||
>
|
||||
<summary className="flex min-h-11 cursor-pointer list-none items-center justify-between gap-5 text-sm font-semibold text-zinc-300 marker:content-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300">
|
||||
<span>Advanced setup for npm, manual configuration, and other MCP clients</span>
|
||||
<ChevronDown className="h-4 w-4 text-zinc-500 transition-transform group-open:rotate-180" aria-hidden="true" />
|
||||
</summary>
|
||||
<div className="mt-5 grid gap-6 rounded-xl border border-zinc-800 bg-[#08080a] p-5 lg:grid-cols-2 lg:p-7">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-white">Install from npm</h3>
|
||||
<p className="mt-2 text-sm leading-6 text-zinc-400">Use this route if your assistant does not support a native bundle. It requires Node.js {product.nodeVersion}+.</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-zinc-800 bg-black p-4 text-xs leading-6 text-emerald-300"><code>npm install -g premiere-pro-mcp{`\n`}premiere-pro-mcp --install-cep</code></pre>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-white">Manual server configuration</h3>
|
||||
<p className="mt-2 text-sm leading-6 text-zinc-400">Only use this when your client asks for a command. Keep Premiere, the connector, and the client on the same computer.</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-zinc-800 bg-black p-4 text-xs leading-6 text-zinc-300"><code>{`{\n "mcpServers": {\n "premiere-pro": {\n "command": "premiere-pro-mcp"\n }\n }\n}`}</code></pre>
|
||||
</div>
|
||||
<div className="lg:col-span-2">
|
||||
<a href={product.links.readme} target="_blank" rel="noopener noreferrer" className="inline-flex min-h-11 items-center gap-2 text-sm font-semibold text-purple-200 transition-colors hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08080a]">
|
||||
Read the full technical guide <ArrowUpRight className="h-4 w-4" aria-hidden="true" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
"use client"
|
||||
|
||||
import { Clapperboard, Film, WandSparkles } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import { useState } from "react"
|
||||
import { product } from "@/lib/product"
|
||||
import { trackOnboardingEvent } from "@/lib/onboarding-events"
|
||||
|
||||
const outcomes = [
|
||||
{ icon: Film, label: "Timeline", detail: "B-roll inserted on V2" },
|
||||
{ icon: WandSparkles, label: "Finish", detail: "Grade and title applied" },
|
||||
{ icon: Clapperboard, label: "Delivery", detail: "ProRes export queued" },
|
||||
]
|
||||
|
||||
export function DemoVideoSection() {
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
|
||||
function startDemo() {
|
||||
setIsPlaying(true)
|
||||
trackOnboardingEvent("marketing_demo_played", { demo: "illustrated_workflow" })
|
||||
}
|
||||
|
||||
return (
|
||||
<section id="demo" className="reveal-section overflow-hidden border-y border-zinc-900 bg-[#050506] px-5 py-24 md:py-32">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="grid items-end gap-8 md:grid-cols-[1fr_auto]">
|
||||
<div className="max-w-3xl">
|
||||
<p className="font-mono text-xs font-semibold uppercase tracking-[0.18em] text-purple-300">Illustrated product walkthrough</p>
|
||||
<h2 className="mt-4 text-balance text-4xl font-bold tracking-[-0.035em] text-white md:text-6xl">
|
||||
From prompt to an explicit result.
|
||||
</h2>
|
||||
</div>
|
||||
<p className="max-w-sm text-sm leading-7 text-zinc-400 md:text-right">
|
||||
See the intended request-to-result flow. This animation is not a recording of a live Premiere host session.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="demo-video-shell relative mt-14 overflow-hidden rounded-2xl border border-white/10 bg-black shadow-[0_38px_100px_rgba(0,0,0,0.58)]">
|
||||
{isPlaying ? (
|
||||
<video
|
||||
className="aspect-video w-full bg-[#060608]"
|
||||
controls
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
preload="metadata"
|
||||
poster="/premiere-pro-mcp-demo-poster.png"
|
||||
aria-label="MCP for Adobe Premiere Pro inserts B-roll, applies a color grade and title, then queues a ProRes export"
|
||||
>
|
||||
<source src="/premiere-pro-mcp-demo.mp4" type="video/mp4" />
|
||||
Your browser does not support embedded video. The demo shows an AI request becoming a structured Premiere Pro edit.
|
||||
</video>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={startDemo}
|
||||
className="group relative block aspect-video w-full overflow-hidden bg-[#060608] text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:ring-inset"
|
||||
aria-label="Play the illustrated MCP for Adobe Premiere Pro workflow walkthrough"
|
||||
>
|
||||
<Image
|
||||
src="/premiere-pro-mcp-demo-poster.png"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, 1152px"
|
||||
className="h-full w-full object-cover opacity-85 transition duration-300 group-hover:scale-[1.01] group-hover:opacity-100"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span className="absolute inset-0 grid place-items-center bg-black/20">
|
||||
<span className="rounded-full border border-white/30 bg-black/75 px-5 py-3 text-sm font-semibold text-white shadow-lg backdrop-blur transition group-hover:border-purple-300 group-hover:bg-black/90">
|
||||
Play illustrated walkthrough
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
<span className="pointer-events-none absolute right-[3%] top-[4%] rounded bg-[#09090d] px-2 py-1 font-mono text-[9px] text-zinc-400 sm:text-[11px]">
|
||||
LOCAL BRIDGE · {product.coreToolCount} TOOLS
|
||||
</span>
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-purple-300/60 to-transparent" aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
<div className="mt-8 grid border-t border-zinc-800 sm:grid-cols-3">
|
||||
{outcomes.map((outcome) => (
|
||||
<div key={outcome.label} className="flex items-center gap-4 border-b border-zinc-800 py-5 sm:border-b-0 sm:border-r sm:px-6 first:sm:pl-0 last:sm:border-r-0">
|
||||
<outcome.icon className="h-5 w-5 shrink-0 text-purple-300" strokeWidth={1.6} />
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-zinc-500">{outcome.label}</p>
|
||||
<p className="mt-1 text-sm text-zinc-200">{outcome.detail}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import { Captions, Check, Layers3, Play, Sparkles } from "lucide-react"
|
||||
|
||||
const clips = [
|
||||
{ label: "Interview_A", start: "2%", width: "31%", color: "bg-[#6557c8]", delay: "80ms" },
|
||||
{ label: "B-roll_City", start: "35%", width: "25%", color: "bg-[#7b66d9]", delay: "180ms" },
|
||||
{ label: "Product_Closeup", start: "62%", width: "36%", color: "bg-[#8f72e8]", delay: "280ms" },
|
||||
] as const
|
||||
|
||||
const brollClips = [
|
||||
{ label: "B-roll_01", start: "14%", width: "23%", delay: "320ms" },
|
||||
{ label: "B-roll_02", start: "44%", width: "28%", delay: "400ms" },
|
||||
] as const
|
||||
|
||||
const waveform = [8, 14, 10, 19, 12, 23, 16, 9, 18, 25, 13, 21, 10, 17, 24, 12, 20, 15, 9, 18, 13, 22, 11, 16]
|
||||
|
||||
export function EditingTimeline() {
|
||||
return (
|
||||
<div
|
||||
className="hero-enter hero-enter-4 mx-auto mt-10 max-w-4xl overflow-hidden rounded-xl border border-zinc-800 bg-[#08080a] shadow-[0_24px_64px_rgba(0,0,0,0.4)] md:mt-14"
|
||||
aria-label="Illustrated Premiere Pro editing workflow"
|
||||
>
|
||||
<div className="flex h-11 items-center border-b border-zinc-800 bg-[#111114] px-4">
|
||||
<div className="flex gap-1.5" aria-hidden="true">
|
||||
<span className="h-2.5 w-2.5 rounded-full bg-red-500/75" />
|
||||
<span className="h-2.5 w-2.5 rounded-full bg-amber-400/75" />
|
||||
<span className="h-2.5 w-2.5 rounded-full bg-emerald-500/75" />
|
||||
</div>
|
||||
<span className="ml-4 font-mono text-[11px] text-zinc-500">Sequence 01 · Premiere Pro</span>
|
||||
<span className="status-pulse ml-auto inline-flex items-center gap-1.5 rounded-full border border-emerald-400/20 bg-emerald-400/8 px-2.5 py-1 text-[10px] font-medium text-emerald-300">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-emerald-400" />
|
||||
MCP connected
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-[0.72fr_1.28fr]">
|
||||
<div className="relative min-h-44 overflow-hidden bg-[#050506] p-4 md:min-h-72 md:border-r md:p-5">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_60%_28%,rgba(139,124,255,0.12),transparent_43%)]" />
|
||||
<div className="relative flex h-full flex-col">
|
||||
<div className="flex items-center justify-between font-mono text-[11px] uppercase tracking-[0.16em] text-zinc-500">
|
||||
<span>Program</span>
|
||||
<span>00:00:08:12</span>
|
||||
</div>
|
||||
|
||||
<div className="my-auto py-4 md:py-6">
|
||||
<div className="timeline-copy-enter mx-auto max-w-[15rem]">
|
||||
<p className="text-balance text-center text-xl font-semibold leading-tight tracking-[-0.03em] text-white md:text-2xl">
|
||||
Keep the creative call.
|
||||
<span className="block text-violet-200">Automate the repeatable work.</span>
|
||||
</p>
|
||||
<div className="mx-auto mt-5 h-px w-16 bg-violet-300/60" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden items-center justify-center gap-4 text-zinc-500 md:flex" aria-hidden="true">
|
||||
<span className="font-mono text-[10px]">1/2</span>
|
||||
<span className="grid h-7 w-7 place-items-center rounded-full bg-zinc-800 text-zinc-200">
|
||||
<Play className="ml-0.5 h-3 w-3 fill-current" />
|
||||
</span>
|
||||
<span className="font-mono text-[10px]">Fit</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden min-w-0 bg-[#0b0b0e] md:block" aria-hidden="true">
|
||||
<div className="flex h-10 items-center justify-between border-b border-zinc-800 px-3">
|
||||
<div className="flex items-center gap-2 text-[10px] text-zinc-500">
|
||||
<Layers3 className="h-3.5 w-3.5 text-violet-300" />
|
||||
<span className="font-medium text-zinc-300">Timeline</span>
|
||||
<span>1080p · 23.976 fps</span>
|
||||
</div>
|
||||
<span className="timeline-copy-enter inline-flex items-center gap-1 rounded bg-violet-300/10 px-2 py-1 text-[9px] text-violet-100" style={{ animationDelay: "750ms" }}>
|
||||
<Sparkles className="h-3 w-3" />
|
||||
B-roll matched
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative p-3 pl-12">
|
||||
<div className="absolute inset-y-3 left-0 w-10 border-r border-zinc-800 bg-[#0d0d10]">
|
||||
{["V2", "V1", "A1"].map((track) => (
|
||||
<div key={track} className="flex h-12 items-center justify-center border-b border-zinc-800 font-mono text-[9px] text-zinc-600">
|
||||
{track}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="relative overflow-hidden">
|
||||
<div className="mb-1 flex h-4 justify-between border-b border-zinc-800 font-mono text-[8px] text-zinc-700">
|
||||
<span>00:00</span>
|
||||
<span>00:05</span>
|
||||
<span>00:10</span>
|
||||
<span>00:15</span>
|
||||
</div>
|
||||
|
||||
<div className="relative h-12 border-b border-zinc-800/80">
|
||||
{brollClips.map((clip) => (
|
||||
<div
|
||||
key={clip.label}
|
||||
className="timeline-clip-enter absolute top-1 flex h-9 origin-left items-center overflow-hidden rounded-sm border border-pink-300/20 bg-[#874c78] px-2 text-[9px] text-pink-100"
|
||||
style={{ left: clip.start, width: clip.width, animationDelay: clip.delay }}
|
||||
>
|
||||
<Captions className="mr-1.5 h-3 w-3 shrink-0 opacity-60" />
|
||||
<span className="truncate">{clip.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="relative h-12 border-b border-zinc-800/80">
|
||||
{clips.map((clip) => (
|
||||
<div
|
||||
key={clip.label}
|
||||
className={`timeline-clip-enter absolute top-1 flex h-9 origin-left items-center overflow-hidden rounded-sm border border-white/10 px-2 text-[9px] text-violet-100 ${clip.color}`}
|
||||
style={{ left: clip.start, width: clip.width, animationDelay: clip.delay }}
|
||||
>
|
||||
<Sparkles className="mr-1.5 h-3 w-3 shrink-0 opacity-60" />
|
||||
<span className="truncate">{clip.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="relative flex h-12 items-center gap-[3px] overflow-hidden border-b border-zinc-800/80 bg-emerald-500/[0.03] px-1">
|
||||
{waveform.map((height, index) => (
|
||||
<span
|
||||
key={`${height}-${index}`}
|
||||
className="timeline-waveform-bar min-w-[2px] flex-1 rounded-full bg-emerald-400/45"
|
||||
style={{ height, animationDelay: `${index * 25}ms`, animationDuration: `${1.2 + (index % 4) * 0.16}s` }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="timeline-playhead pointer-events-none absolute bottom-0 top-3 z-20 w-px bg-[#f2a7d2] shadow-[0_0_10px_rgba(242,167,210,0.65)]" style={{ left: "4%" }}>
|
||||
<span className="absolute -left-[3px] -top-1 h-0 w-0 border-l-[3px] border-r-[3px] border-t-[5px] border-l-transparent border-r-transparent border-t-[#f2a7d2]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="timeline-copy-enter flex items-center justify-between border-t border-zinc-800 bg-[#0f0f12] px-4 py-2.5 text-[10px]" style={{ animationDelay: "900ms" }}>
|
||||
<span className="inline-flex items-center gap-1.5 text-zinc-500">
|
||||
<Check className="h-3 w-3 text-emerald-400" />
|
||||
3 edits applied and verified
|
||||
</span>
|
||||
<span className="font-mono text-zinc-600">ProRes 422</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { ArrowUpRight } from "lucide-react"
|
||||
import { product } from "@/lib/product"
|
||||
|
||||
export const faqItems = [
|
||||
{
|
||||
question: "What is MCP for Adobe Premiere Pro?",
|
||||
answer:
|
||||
"MCP for Adobe Premiere Pro is an open-source Model Context Protocol server that gives compatible AI clients a structured set of tools for editing timelines, applying effects, managing media, and exporting from Adobe Premiere Pro.",
|
||||
},
|
||||
{
|
||||
question: "Which Premiere Pro versions and operating systems are supported?",
|
||||
answer:
|
||||
`The signed CEP connector is the default route for Adobe Premiere Pro ${product.premiereCompatibility} on macOS and Windows, including Apple Silicon and Intel Macs. The UXP bridge adds capability-gated workflows on compatible Premiere ${product.uxpMinimumVersion}+ hosts; it is not the default installer.`,
|
||||
},
|
||||
{
|
||||
question: "Does MCP for Adobe Premiere Pro upload my footage?",
|
||||
answer:
|
||||
"The recommended setup is local-first. Premiere Pro, the connector, and the MCP server run on your machine, and the bridge exchanges commands and structured results rather than automatically uploading project media. Your AI assistant’s separate privacy settings still apply.",
|
||||
},
|
||||
{
|
||||
question: "Which AI clients can connect to it?",
|
||||
answer:
|
||||
"Claude Desktop has a released self-contained bundle and is the recommended starting point. Cursor, VS Code / Copilot, Windsurf, and other MCP-compatible clients can use the local server route. A native one-click installer for every client is not currently shipped.",
|
||||
},
|
||||
{
|
||||
question: "Can I use the MCP server remotely?",
|
||||
answer:
|
||||
"A remote HTTP transport is available, but it requires authentication and a working connection back to the local Premiere Pro bridge. For most editors, the local setup is simpler and safer.",
|
||||
},
|
||||
]
|
||||
|
||||
export function FaqSection() {
|
||||
return (
|
||||
<section id="faq" className="reveal-section bg-black px-5 py-24 md:py-32">
|
||||
<div className="mx-auto grid max-w-6xl gap-14 lg:grid-cols-[0.7fr_1.3fr] lg:gap-20">
|
||||
<div>
|
||||
<h2 className="text-balance text-3xl font-bold tracking-tight text-white md:text-5xl">
|
||||
MCP for Adobe Premiere Pro <span className="text-purple-400">FAQ</span>
|
||||
</h2>
|
||||
<p className="mt-5 max-w-md text-base leading-7 text-zinc-400">
|
||||
Straight answers about compatibility, privacy, AI clients, and remote access.
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/leancoderkavy/premiere-pro-mcp#readme"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-7 inline-flex items-center gap-2 text-sm font-semibold text-purple-300 transition-colors hover:text-purple-200"
|
||||
>
|
||||
Read the complete documentation <ArrowUpRight className="h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-zinc-800 border-y border-zinc-800">
|
||||
{faqItems.map((item) => (
|
||||
<details key={item.question} className="faq-item group py-6">
|
||||
<summary className="flex cursor-pointer list-none items-start justify-between gap-6 text-base font-semibold text-zinc-100 marker:content-none">
|
||||
{item.question}
|
||||
<span className="mt-0.5 text-xl font-light leading-none text-purple-300 transition-transform duration-300 group-open:rotate-45" aria-hidden="true">
|
||||
+
|
||||
</span>
|
||||
</summary>
|
||||
<p className="max-w-2xl pt-4 text-sm leading-7 text-zinc-400">{item.answer}</p>
|
||||
</details>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { Code2, FolderOpen, KeyRound, Scissors, SlidersHorizontal, Upload } from "lucide-react"
|
||||
|
||||
const capabilities = [
|
||||
{
|
||||
icon: Scissors,
|
||||
title: "Edit timelines with intent",
|
||||
description: "Insert, overwrite, trim, split, move, ripple-delete, roll, slide, and slip across sequences and tracks.",
|
||||
},
|
||||
{
|
||||
icon: SlidersHorizontal,
|
||||
title: "Apply repeatable finishing work",
|
||||
description: "Apply effects, adjust Lumetri parameters, load LUTs, stabilize clips, and automate repetitive grading steps.",
|
||||
},
|
||||
{
|
||||
icon: KeyRound,
|
||||
title: "Control keyframes and motion",
|
||||
description: "Add, update, and inspect keyframes with interpolation controls for repeatable animation work.",
|
||||
},
|
||||
{
|
||||
icon: FolderOpen,
|
||||
title: "Organize project structure",
|
||||
description: "Import footage, manage bins, create sequences from presets, inspect metadata, and work with proxies.",
|
||||
},
|
||||
{
|
||||
icon: Upload,
|
||||
title: "Prepare exports with explicit settings",
|
||||
description: "Queue sequences and project items through Adobe Media Encoder using the presets you choose.",
|
||||
},
|
||||
{
|
||||
icon: Code2,
|
||||
title: "Use documented extension seams",
|
||||
description: "Use the structured MCP surface or run custom ExtendScript and QE DOM workflows when you need deeper control.",
|
||||
},
|
||||
]
|
||||
|
||||
export function FeaturesSection() {
|
||||
return (
|
||||
<section id="features" className="reveal-section border-y border-zinc-900 bg-[#050506] px-5 py-24 md:py-32">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="max-w-2xl">
|
||||
<h2 className="text-balance text-3xl font-bold tracking-tight text-white md:text-5xl">
|
||||
A narrower interface for <span className="text-violet-300">predictable edit work.</span>
|
||||
</h2>
|
||||
<p className="mt-5 text-lg leading-8 text-zinc-400">
|
||||
Give an MCP-compatible client structured operations instead of asking it to infer state from Premiere's interface.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 grid gap-x-8 gap-y-12 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{capabilities.map((capability) => (
|
||||
<article key={capability.title} className="group border-t border-zinc-800 pt-6">
|
||||
<capability.icon className="h-6 w-6 text-violet-300 transition-transform duration-200 group-hover:-translate-y-0.5 motion-reduce:transform-none" strokeWidth={1.6} />
|
||||
<h3 className="mt-6 text-lg font-semibold text-zinc-100">{capability.title}</h3>
|
||||
<p className="mt-3 text-sm leading-7 text-zinc-400">{capability.description}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { ArrowRight, Github, ShieldCheck } from "lucide-react"
|
||||
import { TrackedLink } from "@/components/ui/tracked-link"
|
||||
import { product } from "@/lib/product"
|
||||
|
||||
export function FinalCtaSection() {
|
||||
return (
|
||||
<section className="reveal-section border-t border-zinc-900 bg-[#050506] px-5 py-20 md:py-28">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="grid items-end gap-10 border-b border-zinc-800 pb-12 lg:grid-cols-[1fr_auto]">
|
||||
<div className="max-w-3xl">
|
||||
<p className="font-mono text-xs font-semibold uppercase tracking-[0.18em] text-violet-200">
|
||||
Free · open source · local-first
|
||||
</p>
|
||||
<h2 className="mt-4 text-balance text-4xl font-bold tracking-[-0.04em] text-white md:text-6xl">
|
||||
Start with a connection you can verify.
|
||||
</h2>
|
||||
<p className="mt-5 max-w-2xl text-lg leading-8 text-zinc-400">
|
||||
Connect your assistant, verify the local bridge without making changes, then preview the first edit before you apply it.
|
||||
</p>
|
||||
</div>
|
||||
<TrackedLink
|
||||
href="#install"
|
||||
trackingLocation="final_cta"
|
||||
trackingDestination="safe_connection_check"
|
||||
className="inline-flex min-h-12 items-center justify-center gap-2 rounded-md bg-violet-200 px-6 text-sm font-semibold text-black transition-colors hover:bg-white"
|
||||
>
|
||||
Choose your assistant
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</TrackedLink>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 pt-6 text-sm text-zinc-500 sm:flex-row sm:items-center sm:gap-8">
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<ShieldCheck className="h-4 w-4 text-violet-200" />
|
||||
Your media stays local
|
||||
</span>
|
||||
<a
|
||||
href="https://github.com/leancoderkavy/premiere-pro-mcp"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 transition-colors hover:text-white"
|
||||
>
|
||||
<Github className="h-4 w-4" />
|
||||
MIT licensed on GitHub
|
||||
</a>
|
||||
<span>Premiere Pro {product.premiereCompatibility} · macOS and Windows</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Github, Package } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="bg-black px-5 py-12">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="grid gap-10 border-b border-zinc-900 pb-10 md:grid-cols-[1fr_auto_auto] md:gap-16">
|
||||
<div className="max-w-sm">
|
||||
<div className="flex items-center gap-3">
|
||||
<Image
|
||||
src="/marketing/premiere-pro-mcp-mark-v1.png"
|
||||
alt=""
|
||||
width={32}
|
||||
height={32}
|
||||
className="h-8 w-8 object-contain"
|
||||
/>
|
||||
<span className="text-sm font-semibold text-white">premiere-pro-mcp</span>
|
||||
</div>
|
||||
<p className="mt-4 text-sm leading-6 text-zinc-500">Open-source, local-first structured AI control for supported Adobe Premiere Pro workflows.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.16em] text-zinc-600">Product</p>
|
||||
<div className="mt-4 flex flex-col gap-3 text-sm text-zinc-400">
|
||||
<Link href="/#demo" className="hover:text-white">Demo</Link>
|
||||
<Link href="/#features" className="hover:text-white">Features</Link>
|
||||
<Link href="/#how-it-works" className="hover:text-white">How it works</Link>
|
||||
<Link href="/#install" className="hover:text-white">Install</Link>
|
||||
<Link href="/#faq" className="hover:text-white">FAQ</Link>
|
||||
<Link href="/premiere-pro-collaboration-workflow/" className="hover:text-white">Workflow fit guide</Link>
|
||||
<Link href="/project-intake/" className="hover:text-white">Project Intake</Link>
|
||||
<Link href="/facts/" className="hover:text-white">Canonical facts</Link>
|
||||
<Link href="/blog/" className="hover:text-white">Guides</Link>
|
||||
<a href="/docs/" className="hover:text-white">Documentation</a>
|
||||
<a href="/changelog/" className="hover:text-white">Changelog</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.16em] text-zinc-600">Resources</p>
|
||||
<div className="mt-4 flex flex-col gap-3 text-sm text-zinc-400">
|
||||
<a href="https://github.com/leancoderkavy/premiere-pro-mcp" target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-2 hover:text-white"><Github className="h-4 w-4" /> GitHub</a>
|
||||
<a href="https://www.npmjs.com/package/premiere-pro-mcp" target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-2 hover:text-white"><Package className="h-4 w-4" /> npm package</a>
|
||||
<a href="https://github.com/leancoderkavy/premiere-pro-mcp/blob/main/SECURITY.md" target="_blank" rel="noopener noreferrer" className="hover:text-white">Security</a>
|
||||
<a href="/privacy/" className="hover:text-white">Privacy</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 pt-6 text-xs text-zinc-600 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p>© 2026 premiere-pro-mcp contributors. MIT licensed.</p>
|
||||
<p>Not affiliated with Adobe Inc. Adobe Premiere Pro is a trademark of Adobe Inc.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import { Github, Monitor, Package, ShieldCheck } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import { MobileNav } from "@/components/sections/mobile-nav"
|
||||
import { WorkflowProof } from "@/components/sections/workflow-proof"
|
||||
import { TrackedLink } from "@/components/ui/tracked-link"
|
||||
import { product } from "@/lib/product"
|
||||
|
||||
const navItems = [
|
||||
{ label: "Demo", href: "#demo" },
|
||||
{ label: "Features", href: "#features" },
|
||||
{ label: "How it works", href: "#how-it-works" },
|
||||
{ label: "Project Intake", href: "/project-intake/" },
|
||||
{ label: "Install", href: "#install" },
|
||||
{ label: "FAQ", href: "#faq" },
|
||||
{ label: "Guides", href: "/blog/" },
|
||||
{ label: "Docs", href: "/docs/" },
|
||||
{ label: "Changelog", href: "/changelog/" },
|
||||
]
|
||||
|
||||
const proofItems = [
|
||||
{ icon: Package, title: `v${product.version}`, detail: "Current release" },
|
||||
{ icon: Monitor, title: "macOS + Windows", detail: "Supported desktop hosts" },
|
||||
{ icon: ShieldCheck, title: "Local-first", detail: "Media stays on your machine" },
|
||||
{ icon: Github, title: "MIT licensed", detail: "Source available on GitHub" },
|
||||
]
|
||||
|
||||
export function HeroSection() {
|
||||
return (
|
||||
<>
|
||||
<header className="fixed inset-x-0 top-0 z-50 border-b border-white/10 bg-black/95">
|
||||
<nav className="mx-auto flex h-16 max-w-6xl items-center justify-between px-4 sm:px-5" aria-label="Primary navigation">
|
||||
<a href="#top" className="flex items-center gap-3 text-sm font-semibold text-white">
|
||||
<Image
|
||||
src="/marketing/premiere-pro-mcp-mark-v1.png"
|
||||
alt=""
|
||||
width={32}
|
||||
height={32}
|
||||
className="h-8 w-8 object-contain"
|
||||
priority
|
||||
/>
|
||||
<span>premiere-pro-mcp</span>
|
||||
</a>
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="hidden items-center gap-5 lg:flex">
|
||||
{navItems.map((item) => (
|
||||
<a key={item.href} href={item.href} className="text-sm text-zinc-400 transition-colors hover:text-white">
|
||||
{item.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<MobileNav />
|
||||
<TrackedLink
|
||||
href="https://github.com/leancoderkavy/premiere-pro-mcp"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
trackingLocation="navigation"
|
||||
trackingDestination="github"
|
||||
aria-label="premiere-pro-mcp on GitHub"
|
||||
className="inline-flex min-h-11 items-center gap-2 rounded-md border border-zinc-700 px-3 py-2 text-sm font-medium text-zinc-200 transition-colors hover:border-zinc-500 hover:bg-zinc-900"
|
||||
>
|
||||
<Github className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">GitHub</span>
|
||||
</TrackedLink>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<section id="top" className="relative overflow-hidden px-4 pb-16 pt-28 sm:px-5 md:pb-28 md:pt-40">
|
||||
<div className="hero-grid absolute inset-0" aria-hidden="true" />
|
||||
<div className="hero-glow absolute left-1/2 top-0 h-[30rem] w-full max-w-[52rem] -translate-x-1/2" aria-hidden="true" />
|
||||
<div className="hero-timeline absolute inset-x-0 top-24 mx-auto hidden max-w-5xl md:block" aria-hidden="true">
|
||||
<span className="timeline-rule timeline-rule-one" />
|
||||
<span className="timeline-rule timeline-rule-two" />
|
||||
<span className="timeline-clip timeline-clip-one" />
|
||||
<span className="timeline-clip timeline-clip-two" />
|
||||
<span className="timeline-clip timeline-clip-three" />
|
||||
</div>
|
||||
|
||||
<div className="relative mx-auto max-w-6xl">
|
||||
<div className="mx-auto max-w-4xl text-center">
|
||||
<p className="hero-enter hero-enter-1 mb-5 font-mono text-xs font-semibold uppercase tracking-[0.18em] text-violet-200">
|
||||
Open source · local bridge · explicit confirmation
|
||||
</p>
|
||||
<h1 className="hero-enter hero-enter-1 text-balance text-4xl font-bold leading-[1.02] tracking-[-0.045em] text-white sm:text-6xl md:text-7xl">
|
||||
MCP for Adobe Premiere Pro: make Premiere changes you can <span className="accent-text">inspect before you apply.</span>
|
||||
</h1>
|
||||
<p className="hero-enter hero-enter-2 mx-auto mt-7 max-w-2xl text-balance text-lg leading-8 text-zinc-400 md:text-xl">
|
||||
Connect the MCP-compatible client you already use to a local Premiere bridge. Review the target and plan first, then confirm supported work with current project state.
|
||||
</p>
|
||||
<div className="hero-enter hero-enter-3 mt-9 flex flex-col items-center justify-center gap-3 sm:flex-row">
|
||||
<TrackedLink
|
||||
href="#install"
|
||||
trackingLocation="hero"
|
||||
trackingDestination="safe_connection_check"
|
||||
className="inline-flex min-h-12 items-center justify-center gap-2 rounded-md bg-violet-200 px-6 text-sm font-semibold text-black transition-colors hover:bg-white"
|
||||
>
|
||||
<Package className="h-4 w-4" /> Verify your connection
|
||||
</TrackedLink>
|
||||
<TrackedLink
|
||||
href="/project-intake/"
|
||||
trackingLocation="hero"
|
||||
trackingDestination="project_intake_workflow"
|
||||
className="inline-flex min-h-12 items-center justify-center gap-2 rounded-md border border-zinc-700 bg-zinc-950/80 px-6 text-sm font-semibold text-zinc-100 transition-colors hover:border-zinc-500 hover:bg-zinc-900"
|
||||
>
|
||||
<ShieldCheck className="h-4 w-4" /> See Project Intake
|
||||
</TrackedLink>
|
||||
</div>
|
||||
<p className="hero-enter hero-enter-3 mt-4 text-sm text-zinc-500">
|
||||
Project context is opt-in and local. Applied plans require current targets and confirmation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="hero-enter hero-enter-4">
|
||||
<WorkflowProof />
|
||||
</div>
|
||||
|
||||
<div className="hero-enter hero-enter-5 mt-6 flex snap-x overflow-x-auto rounded-xl border border-zinc-800 bg-zinc-950/75 sm:mt-8 sm:grid sm:grid-cols-2 sm:overflow-hidden lg:grid-cols-4">
|
||||
{proofItems.map((item) => (
|
||||
<div key={item.title} className="flex min-w-[13rem] snap-start items-center gap-3 border-r border-zinc-800 px-5 py-4 last:border-r-0 sm:min-w-0 sm:border-b sm:even:border-l lg:border-b-0 lg:border-l first:lg:border-l-0">
|
||||
<item.icon className="h-5 w-5 shrink-0 text-purple-400" strokeWidth={1.7} />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-zinc-100">{item.title}</p>
|
||||
<p className="mt-0.5 text-xs text-zinc-500">{item.detail}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { BorderBeam } from "@/components/ui/border-beam"
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
label: "Local (stdio)",
|
||||
steps: [
|
||||
{ cmd: "npm install -g premiere-pro-mcp", comment: "# Install globally" },
|
||||
{ cmd: "premiere-pro-mcp --install-cep", comment: "# Install the CEP panel into Premiere" },
|
||||
],
|
||||
config: `{
|
||||
"mcpServers": {
|
||||
"premiere-pro": {
|
||||
"command": "premiere-pro-mcp"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
note: "Works with Claude Desktop, Windsurf, Cursor, and any MCP client that supports stdio.",
|
||||
},
|
||||
{
|
||||
label: "Remote (Fly.io)",
|
||||
steps: [
|
||||
{ cmd: "npm install -g mcp-remote", comment: "# Install mcp-remote proxy" },
|
||||
],
|
||||
config: `{
|
||||
"mcpServers": {
|
||||
"premiere-pro": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"mcp-remote",
|
||||
"https://premiere-pro-mcp.fly.dev/mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}`,
|
||||
note: "Note: the file bridge still requires the CEP plugin on the same machine or a shared volume.",
|
||||
},
|
||||
]
|
||||
|
||||
function CodeBlock({ code, label }: { code: string; label?: string }) {
|
||||
return (
|
||||
<div className="relative overflow-hidden rounded-lg border border-zinc-800 bg-zinc-950">
|
||||
<BorderBeam colorFrom="#9999ff" colorTo="#ff6699" duration={10} size={60} />
|
||||
{label && (
|
||||
<div className="border-b border-zinc-800 px-4 py-2">
|
||||
<span className="text-xs text-zinc-500">{label}</span>
|
||||
</div>
|
||||
)}
|
||||
<pre className="overflow-x-auto p-4 text-sm leading-relaxed text-zinc-300">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function InstallSection() {
|
||||
return (
|
||||
<section className="bg-black px-4 py-24">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="mb-16 text-center">
|
||||
<p className="text-sm font-semibold uppercase tracking-widest text-zinc-500">Get started</p>
|
||||
<h2 className="mt-2 text-3xl font-bold text-white md:text-4xl">Two ways to connect</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 md:grid-cols-2">
|
||||
{tabs.map((tab) => (
|
||||
<div key={tab.label} className="space-y-4">
|
||||
<h3 className="text-xl font-semibold text-white">{tab.label}</h3>
|
||||
|
||||
<div className="space-y-2">
|
||||
{tab.steps.map((s) => (
|
||||
<div key={s.cmd} className="overflow-hidden rounded-lg border border-zinc-800 bg-zinc-950 px-4 py-3 font-mono text-sm">
|
||||
<span className="text-zinc-600">{s.comment}{"\n"}</span>
|
||||
<span className="text-green-400">$ {s.cmd}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<CodeBlock code={tab.config} label="mcp config" />
|
||||
|
||||
<p className="text-sm text-zinc-500">{tab.note}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* npm badge row */}
|
||||
<div className="mt-16 flex flex-wrap items-center justify-center gap-4">
|
||||
{[
|
||||
{ label: "npm", href: "https://www.npmjs.com/package/premiere-pro-mcp", badge: "latest" },
|
||||
{ label: "GitHub", href: "https://github.com/leancoderkavy/premiere-pro-mcp", badge: "MIT" },
|
||||
{ label: "MCP", href: "https://modelcontextprotocol.io", badge: "1.27" },
|
||||
{ label: "Node.js", href: "https://nodejs.org", badge: "20.19+" },
|
||||
].map((b) => (
|
||||
<a
|
||||
key={b.label}
|
||||
href={b.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-0 overflow-hidden rounded text-xs font-semibold"
|
||||
>
|
||||
<span className="bg-zinc-700 px-2 py-1 text-zinc-300">{b.label}</span>
|
||||
<span className="bg-purple-700 px-2 py-1 text-white">{b.badge}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client"
|
||||
|
||||
import { Menu, X } from "lucide-react"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
const mobileLinks = [
|
||||
{ label: "Demo", href: "#demo" },
|
||||
{ label: "Features", href: "#features" },
|
||||
{ label: "How it works", href: "#how-it-works" },
|
||||
{ label: "Project Intake", href: "/project-intake/" },
|
||||
{ label: "Install", href: "#install" },
|
||||
{ label: "FAQ", href: "#faq" },
|
||||
{ label: "Guides", href: "/blog/" },
|
||||
{ label: "Docs", href: "/docs/" },
|
||||
{ label: "Changelog", href: "/changelog/" },
|
||||
] as const
|
||||
|
||||
export function MobileNav() {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
|
||||
function closeOnEscape(event: KeyboardEvent) {
|
||||
if (event.key === "Escape") setOpen(false)
|
||||
}
|
||||
|
||||
window.addEventListener("keydown", closeOnEscape)
|
||||
return () => window.removeEventListener("keydown", closeOnEscape)
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
<div className="relative lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((current) => !current)}
|
||||
aria-expanded={open}
|
||||
aria-controls="mobile-navigation"
|
||||
aria-label={open ? "Close navigation" : "Open navigation"}
|
||||
className="grid h-10 w-10 place-items-center rounded-md border border-zinc-700 text-zinc-200 transition-colors hover:border-zinc-500 hover:bg-zinc-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-400"
|
||||
>
|
||||
{open ? <X className="h-4 w-4" /> : <Menu className="h-4 w-4" />}
|
||||
</button>
|
||||
<div
|
||||
id="mobile-navigation"
|
||||
aria-hidden={!open}
|
||||
className={`absolute right-0 top-12 w-56 origin-top-right overflow-hidden rounded-lg border border-zinc-800 bg-[#0b0b0e] p-2 shadow-2xl transition-[opacity,transform,visibility] duration-200 ease-out motion-reduce:transition-none ${
|
||||
open ? "visible scale-100 opacity-100" : "invisible scale-[0.98] opacity-0 pointer-events-none"
|
||||
}`}
|
||||
>
|
||||
{mobileLinks.map((item) => (
|
||||
<a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
onClick={() => setOpen(false)}
|
||||
tabIndex={open ? 0 : -1}
|
||||
className="block min-h-11 rounded-md px-3 py-2.5 text-sm text-zinc-300 transition-colors hover:bg-zinc-900 hover:text-white"
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Marquee } from "@/components/ui/marquee"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { product } from "@/lib/product"
|
||||
|
||||
const tools = [
|
||||
{ name: "add_to_timeline", category: "Timeline" },
|
||||
{ name: "apply_effect", category: "Effects" },
|
||||
{ name: "color_correct", category: "Color" },
|
||||
{ name: "export_sequence", category: "Export" },
|
||||
{ name: "add_keyframe", category: "Keyframes" },
|
||||
{ name: "create_sequence", category: "Sequence" },
|
||||
{ name: "import_media", category: "Project" },
|
||||
{ name: "ripple_delete", category: "Advanced" },
|
||||
{ name: "roll_edit", category: "Advanced" },
|
||||
{ name: "apply_lut", category: "Color" },
|
||||
{ name: "capture_frame", category: "Export" },
|
||||
{ name: "stabilize_clip", category: "Effects" },
|
||||
{ name: "execute_extendscript", category: "Scripting" },
|
||||
{ name: "get_project_info", category: "Discovery" },
|
||||
{ name: "set_clip_properties", category: "Timeline" },
|
||||
{ name: "auto_reframe_sequence", category: "Sequence" },
|
||||
{ name: "copy_effects_between_clips", category: "Clipboard" },
|
||||
{ name: "set_keyframe_interpolation", category: "Keyframes" },
|
||||
{ name: "get_active_sequence", category: "Discovery" },
|
||||
{ name: "create_caption_track", category: "Captions" },
|
||||
{ name: "export_aaf", category: "Export" },
|
||||
{ name: "split_clip", category: "Timeline" },
|
||||
{ name: "set_workspace", category: "Workspace" },
|
||||
{ name: "get_premiere_state", category: "Inspection" },
|
||||
]
|
||||
|
||||
const categoryColors: Record<string, string> = {
|
||||
Timeline: "text-blue-400 bg-blue-400/10",
|
||||
Effects: "text-purple-400 bg-purple-400/10",
|
||||
Color: "text-pink-400 bg-pink-400/10",
|
||||
Export: "text-green-400 bg-green-400/10",
|
||||
Keyframes: "text-orange-400 bg-orange-400/10",
|
||||
Sequence: "text-cyan-400 bg-cyan-400/10",
|
||||
Project: "text-yellow-400 bg-yellow-400/10",
|
||||
Advanced: "text-red-400 bg-red-400/10",
|
||||
Scripting: "text-teal-400 bg-teal-400/10",
|
||||
Discovery: "text-indigo-400 bg-indigo-400/10",
|
||||
Clipboard: "text-violet-400 bg-violet-400/10",
|
||||
Captions: "text-lime-400 bg-lime-400/10",
|
||||
Workspace: "text-amber-400 bg-amber-400/10",
|
||||
Inspection:"text-sky-400 bg-sky-400/10",
|
||||
}
|
||||
|
||||
function ToolCard({ name, category }: { name: string; category: string }) {
|
||||
const color = categoryColors[category] ?? "text-zinc-400 bg-zinc-400/10"
|
||||
return (
|
||||
<div className="flex items-center gap-3 rounded-lg border border-zinc-800 bg-zinc-950 px-4 py-2.5 shadow-sm">
|
||||
<span className={cn("rounded px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider", color)}>
|
||||
{category}
|
||||
</span>
|
||||
<code className="text-sm text-zinc-300">{name}</code>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const half = Math.ceil(tools.length / 2)
|
||||
const row1 = tools.slice(0, half)
|
||||
const row2 = tools.slice(half)
|
||||
|
||||
export function ToolsMarquee() {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-black py-20">
|
||||
<div className="mb-12 text-center">
|
||||
<p className="text-sm font-semibold uppercase tracking-widest text-zinc-500">{product.coreToolCount} core tools across 40 modules</p>
|
||||
<h2 className="mt-2 text-3xl font-bold text-white md:text-4xl">Everything the AI needs to edit</h2>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Marquee pauseOnHover className="[--duration:35s]">
|
||||
{row1.map((t) => <ToolCard key={t.name} {...t} />)}
|
||||
</Marquee>
|
||||
<Marquee reverse pauseOnHover className="[--duration:40s]">
|
||||
{row2.map((t) => <ToolCard key={t.name} {...t} />)}
|
||||
</Marquee>
|
||||
</div>
|
||||
|
||||
{/* fade edges */}
|
||||
<div className="pointer-events-none absolute inset-y-0 left-0 w-32 bg-gradient-to-r from-black" />
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 w-32 bg-gradient-to-l from-black" />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { CheckCircle2, ClipboardCheck, FileSearch, ListChecks, ScanSearch, ShieldCheck } from "lucide-react"
|
||||
|
||||
type WorkflowStep = {
|
||||
id: "capture" | "search" | "plan" | "preview"
|
||||
number: string
|
||||
title: string
|
||||
tool: string
|
||||
summary: string
|
||||
boundary: string
|
||||
details: string[]
|
||||
icon: typeof ScanSearch
|
||||
}
|
||||
|
||||
const steps: WorkflowStep[] = [
|
||||
{
|
||||
id: "capture",
|
||||
number: "01",
|
||||
title: "Capture context",
|
||||
tool: "manage_project_context",
|
||||
summary: "Create a bounded local snapshot of the active sequence and source identities.",
|
||||
boundary: "Read-only · opt-in local index",
|
||||
details: [
|
||||
"No context is captured until an MCP client explicitly calls the tool.",
|
||||
"Native project and media paths are hashed before persistence.",
|
||||
"Capture establishes revisions for later stale-state checks.",
|
||||
],
|
||||
icon: ScanSearch,
|
||||
},
|
||||
{
|
||||
id: "search",
|
||||
number: "02",
|
||||
title: "Find evidence",
|
||||
tool: "search_project_context",
|
||||
summary: "Retrieve only the clips, transcript passages, observations, and placements relevant to the request.",
|
||||
boundary: "Read-only · bounded evidence",
|
||||
details: [
|
||||
"Search returns stable Premiere identities and revision provenance.",
|
||||
"The client can limit context to the kinds needed for one editing intent.",
|
||||
"A stale or ambiguous state calls for a fresh capture, not a blind edit.",
|
||||
],
|
||||
icon: FileSearch,
|
||||
},
|
||||
{
|
||||
id: "plan",
|
||||
number: "03",
|
||||
title: "Build the plan",
|
||||
tool: "create_context_edit_plan",
|
||||
summary: "Produce an evidence-backed, non-mutating candidate scaffold before constructing timeline operations.",
|
||||
boundary: "Non-mutating · editorial review required",
|
||||
details: [
|
||||
"Candidates include source/time evidence and stale-state guards.",
|
||||
"The plan is not proof that an editorial decision is correct.",
|
||||
"The editor and chosen AI client can refine the plan before any mutation.",
|
||||
],
|
||||
icon: ListChecks,
|
||||
},
|
||||
{
|
||||
id: "preview",
|
||||
number: "04",
|
||||
title: "Preview, approve, verify",
|
||||
tool: "preview_edit_plan → apply_edit_plan",
|
||||
summary: "Preview the compound edit, then apply only with the exact confirmation token and current targets.",
|
||||
boundary: "Explicit confirmation · post-state verification",
|
||||
details: [
|
||||
"Every target is revalidated before an edit is attempted.",
|
||||
"The preview confirmation token prevents applying a different plan by mistake.",
|
||||
"Returned state or diagnostics are evidence; an attempted command is not enough.",
|
||||
],
|
||||
icon: ClipboardCheck,
|
||||
},
|
||||
]
|
||||
|
||||
export function WorkflowProof() {
|
||||
const [selectedId, setSelectedId] = useState<WorkflowStep["id"]>("capture")
|
||||
const selected = steps.find((step) => step.id === selectedId) ?? steps[0]
|
||||
const SelectedIcon = selected.icon
|
||||
|
||||
return (
|
||||
<section
|
||||
className="relative mx-auto mt-12 max-w-6xl border border-zinc-800 bg-[#08080a] p-5 shadow-[0_28px_90px_rgba(0,0,0,0.45)] sm:mt-16 sm:p-7 lg:p-8"
|
||||
aria-labelledby="project-context-heading"
|
||||
>
|
||||
<div className="flex flex-col gap-5 border-b border-zinc-800 pb-6 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div>
|
||||
<p className="font-mono text-xs font-semibold uppercase tracking-[0.16em] text-purple-300">Illustrated product workflow</p>
|
||||
<h2 id="project-context-heading" className="mt-3 text-2xl font-semibold tracking-tight text-white sm:text-3xl">
|
||||
Keep the context. Review the change.
|
||||
</h2>
|
||||
</div>
|
||||
<p className="max-w-xl text-sm leading-6 text-zinc-400">
|
||||
A local project-context workflow makes the request inspectable from first evidence to returned result. This maps real MCP tools; it is not a recording of a live Premiere session.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid gap-6 lg:grid-cols-[14rem_minmax(0,1fr)]">
|
||||
<ol className="grid gap-2 sm:grid-cols-2 lg:grid-cols-1" aria-label="Project context workflow steps">
|
||||
{steps.map((step) => {
|
||||
const Icon = step.icon
|
||||
const active = step.id === selected.id
|
||||
|
||||
return (
|
||||
<li key={step.id}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedId(step.id)}
|
||||
aria-pressed={active}
|
||||
aria-controls="workflow-step-detail"
|
||||
className={`flex min-h-20 w-full items-center gap-3 border px-3 py-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-300 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08080a] ${
|
||||
active ? "border-purple-400/70 bg-purple-400/[0.09]" : "border-zinc-800 bg-black hover:border-zinc-600"
|
||||
}`}
|
||||
>
|
||||
<span className={`font-mono text-xs ${active ? "text-purple-200" : "text-zinc-500"}`}>{step.number}</span>
|
||||
<Icon className={`h-4 w-4 shrink-0 ${active ? "text-purple-200" : "text-zinc-500"}`} aria-hidden="true" />
|
||||
<span>
|
||||
<span className="block text-sm font-semibold text-zinc-100">{step.title}</span>
|
||||
<span className="mt-1 block font-mono text-[0.65rem] text-zinc-500">{step.tool}</span>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ol>
|
||||
|
||||
<article id="workflow-step-detail" aria-live="polite" className="border border-zinc-800 bg-black p-5 sm:p-7">
|
||||
<div className="flex flex-col gap-5 border-b border-zinc-800 pb-5 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="grid h-10 w-10 shrink-0 place-items-center border border-purple-400/50 bg-purple-400/[0.08] text-purple-200">
|
||||
<SelectedIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
<div>
|
||||
<p className="font-mono text-xs uppercase tracking-[0.14em] text-purple-300">{selected.tool}</p>
|
||||
<h3 className="mt-2 text-xl font-semibold text-white">{selected.title}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<p className="inline-flex w-fit items-center gap-2 border border-emerald-400/30 bg-emerald-400/[0.06] px-3 py-2 text-xs font-medium text-emerald-200">
|
||||
<ShieldCheck className="h-4 w-4" aria-hidden="true" /> {selected.boundary}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="mt-6 max-w-2xl text-base leading-7 text-zinc-300">{selected.summary}</p>
|
||||
<ul className="mt-6 space-y-3 border-t border-zinc-900 pt-5 text-sm leading-6 text-zinc-400">
|
||||
{selected.details.map((detail) => (
|
||||
<li key={detail} className="flex gap-3">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-emerald-300" aria-hidden="true" />
|
||||
<span>{detail}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user