Add grouped marketing feature pages and SEO-friendly feature hub (#199)
* Refine marketing feature pages and feature navigation * Add SEO feature page polish * Share feature slugs with sitemap generation
This commit is contained in:
parent
7a0ab0de2c
commit
a8124a07b5
@ -3,6 +3,7 @@
|
||||
import { existsSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { FEATURE_PAGE_SLUGS } from "../src/lib/feature-page-slugs.js";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@ -19,6 +20,9 @@ const STATIC_PATHS = [
|
||||
"/privacy",
|
||||
"/terms-and-conditions",
|
||||
"/guides",
|
||||
"/features",
|
||||
"/features/mcp",
|
||||
...Object.values(FEATURE_PAGE_SLUGS).map((slug) => `/features/${slug}`),
|
||||
];
|
||||
|
||||
function getGuideEntries(dir = GUIDE_CONTENT_DIR, segments = []) {
|
||||
|
||||
196
web/src/components/feature-page.tsx
Normal file
196
web/src/components/feature-page.tsx
Normal file
@ -0,0 +1,196 @@
|
||||
import type { FeaturePage } from "@/lib/feature-pages";
|
||||
|
||||
type FeaturePageProps = {
|
||||
page: FeaturePage;
|
||||
};
|
||||
|
||||
export function FeaturePageTemplate({ page }: FeaturePageProps) {
|
||||
return (
|
||||
<article>
|
||||
<header>
|
||||
<p className="text-sm font-medium text-neutral-500">{page.eyebrow}</p>
|
||||
<h1 className="mt-3 text-3xl font-bold leading-tight tracking-tight">
|
||||
{page.title}
|
||||
</h1>
|
||||
<p className="mt-4 text-neutral-700 leading-relaxed">
|
||||
{page.description}
|
||||
</p>
|
||||
<div className="mt-5">
|
||||
<a
|
||||
href="https://app.openseo.so/sign-up"
|
||||
className="inline-flex h-10 items-center justify-center rounded-md bg-neutral-900 px-5 text-sm font-medium text-white transition-colors hover:bg-neutral-800"
|
||||
>
|
||||
{page.ctaLabel ?? "Try OpenSEO"}
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<FeatureImage page={page} />
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="text-xl font-semibold">What you can do</h2>
|
||||
<ol className="mt-6 space-y-6">
|
||||
{page.workflows.map((workflow, index) => (
|
||||
<li
|
||||
key={workflow.title}
|
||||
className="grid grid-cols-[2.25rem_1fr] gap-x-4"
|
||||
>
|
||||
<span className="pt-[2px] font-mono text-sm tabular-nums text-neutral-400">
|
||||
{String(index + 1).padStart(2, "0")}
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-neutral-900">
|
||||
{workflow.title}
|
||||
</h3>
|
||||
<p className="mt-1.5 text-sm leading-relaxed text-neutral-600">
|
||||
{workflow.description}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="text-xl font-semibold">Data you can act on</h2>
|
||||
<dl className="mt-5 grid grid-cols-2 border-y border-neutral-200 md:grid-cols-4 md:divide-x md:divide-neutral-200">
|
||||
{page.metrics.map((metric, index) => (
|
||||
<div
|
||||
key={metric.label}
|
||||
className={[
|
||||
"px-4 py-3",
|
||||
index % 2 === 1 && "border-l border-neutral-200 md:border-l-0",
|
||||
index < 2 && "border-b border-neutral-200 md:border-b-0",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")}
|
||||
>
|
||||
<dt className="text-xs text-neutral-500">{metric.label}</dt>
|
||||
<dd className="mt-1 text-sm font-semibold text-neutral-900">
|
||||
{metric.value}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="text-xl font-semibold">Use cases</h2>
|
||||
<ul className="mt-4 space-y-3">
|
||||
{page.useCases.map((item) => (
|
||||
<li key={item} className="flex gap-2.5 text-sm text-neutral-700">
|
||||
<span className="mt-[2px] text-neutral-400">—</span>
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="text-xl font-semibold">Why OpenSEO</h2>
|
||||
<ul className="mt-4 space-y-3">
|
||||
{page.differentiators.map((item) => (
|
||||
<li key={item} className="flex gap-2.5 text-sm text-neutral-700">
|
||||
<span className="mt-[2px] text-neutral-400">—</span>
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="text-xl font-semibold">Related features</h2>
|
||||
<div className="mt-4 grid gap-3 md:grid-cols-3">
|
||||
{page.related.map((item) => (
|
||||
<a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="rounded-lg border border-neutral-200 bg-white p-4 text-sm font-medium text-neutral-900 transition-colors hover:border-neutral-900"
|
||||
>
|
||||
{item.label}
|
||||
<span aria-hidden="true" className="ml-1 text-neutral-500">
|
||||
→
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="text-xl font-semibold">FAQ</h2>
|
||||
<div className="mt-5 divide-y divide-neutral-200 rounded-lg border border-neutral-200 bg-white">
|
||||
{page.faqs.map((faq) => (
|
||||
<div key={faq.question} className="p-4">
|
||||
<h3 className="text-sm font-semibold text-neutral-900">
|
||||
{faq.question}
|
||||
</h3>
|
||||
<p className="mt-1.5 text-sm leading-relaxed text-neutral-600">
|
||||
{faq.answer}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-12 rounded-lg border border-neutral-200 bg-neutral-50 p-5">
|
||||
<h2 className="text-lg font-semibold text-neutral-900">
|
||||
Try OpenSEO
|
||||
</h2>
|
||||
<p className="mt-2 text-sm leading-relaxed text-neutral-600">
|
||||
The open source alternative to bloated, expensive, legacy SEO tools.
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<a
|
||||
href="https://app.openseo.so/sign-up"
|
||||
className="inline-flex h-9 items-center justify-center rounded-md bg-neutral-900 px-4 text-sm font-medium text-white transition-colors hover:bg-neutral-800"
|
||||
>
|
||||
{page.ctaLabel ?? "Try OpenSEO"}
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureImage({ page }: FeaturePageProps) {
|
||||
if (page.imageSrc) {
|
||||
return (
|
||||
<figure className="mt-9">
|
||||
<img
|
||||
src={page.imageSrc}
|
||||
alt={page.imageAlt}
|
||||
width={1600}
|
||||
height={1000}
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
className="aspect-[16/10] w-full rounded-lg border border-neutral-200 object-cover object-top"
|
||||
/>
|
||||
<figcaption className="mt-2 text-[11px] text-neutral-500">
|
||||
{page.eyebrow} in OpenSEO.
|
||||
</figcaption>
|
||||
</figure>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<figure className="mt-9">
|
||||
<div
|
||||
role="img"
|
||||
aria-label={page.imageAlt}
|
||||
className="flex aspect-[16/10] w-full items-center justify-center rounded-lg border border-dashed border-neutral-300 bg-neutral-50 px-6 text-center"
|
||||
>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-neutral-900">
|
||||
{page.imagePlaceholder}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-neutral-500">
|
||||
Replace with final product screenshot or short demo asset.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption className="mt-2 text-[11px] text-neutral-500">
|
||||
Placeholder for final OpenSEO product imagery.
|
||||
</figcaption>
|
||||
</figure>
|
||||
);
|
||||
}
|
||||
10
web/src/lib/feature-page-slugs.js
Normal file
10
web/src/lib/feature-page-slugs.js
Normal file
@ -0,0 +1,10 @@
|
||||
export const FEATURE_PAGE_SLUGS = {
|
||||
keywordResearch: "keyword-research",
|
||||
siteAudit: "site-audit",
|
||||
backlinkChecker: "backlink-checker",
|
||||
domainOverview: "domain-overview",
|
||||
rankTracking: "rank-tracking",
|
||||
savedKeywords: "saved-keywords",
|
||||
aiBrandVisibility: "ai-brand-visibility",
|
||||
aiSearchPrompts: "ai-search-prompts",
|
||||
};
|
||||
646
web/src/lib/feature-pages.ts
Normal file
646
web/src/lib/feature-pages.ts
Normal file
@ -0,0 +1,646 @@
|
||||
import { FEATURE_PAGE_SLUGS } from "@/lib/feature-page-slugs";
|
||||
|
||||
export type FeaturePage = {
|
||||
slug: string;
|
||||
eyebrow: string;
|
||||
navDescription: string;
|
||||
title: string;
|
||||
description: string;
|
||||
primaryKeyword: string;
|
||||
secondaryKeywords: string[];
|
||||
imageAlt: string;
|
||||
imagePlaceholder: string;
|
||||
imageSrc?: string;
|
||||
ctaLabel?: string;
|
||||
workflows: Array<{
|
||||
title: string;
|
||||
description: string;
|
||||
}>;
|
||||
metrics: Array<{
|
||||
label: string;
|
||||
value: string;
|
||||
}>;
|
||||
useCases: string[];
|
||||
differentiators: string[];
|
||||
related: Array<{
|
||||
label: string;
|
||||
href: string;
|
||||
}>;
|
||||
faqs: Array<{
|
||||
question: string;
|
||||
answer: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export const featurePages = {
|
||||
keywordResearch: {
|
||||
slug: FEATURE_PAGE_SLUGS.keywordResearch,
|
||||
eyebrow: "Keyword Research",
|
||||
navDescription: "Find keyword ideas and SERPs.",
|
||||
title: "Keyword research tool for practical SEO planning",
|
||||
description:
|
||||
"Find keyword ideas, compare search volume and difficulty, inspect SERP results, and save the opportunities worth building around.",
|
||||
primaryKeyword: "keyword research tool",
|
||||
secondaryKeywords: [
|
||||
"seo keyword research tool",
|
||||
"free keyword research tool",
|
||||
"keyword research tools",
|
||||
],
|
||||
imageAlt: "OpenSEO keyword research dashboard",
|
||||
imagePlaceholder: "Keyword research screenshot placeholder",
|
||||
imageSrc:
|
||||
"https://imagedelivery.net/ysLOa6bzFaM49Jxok-TAlw/d77077d0-cdf4-4523-0c41-56a7b4861300/public",
|
||||
workflows: [
|
||||
{
|
||||
title: "Research seed topics",
|
||||
description:
|
||||
"Start with one or more seeds and expand them into keyword ideas with volume, difficulty, CPC, and intent signals.",
|
||||
},
|
||||
{
|
||||
title: "Inspect the real SERP",
|
||||
description:
|
||||
"Open SERP results beside keyword metrics so content decisions are based on the pages ranking for that query.",
|
||||
},
|
||||
{
|
||||
title: "Save and organize opportunities",
|
||||
description:
|
||||
"Keep useful keywords in your workspace and tag them for content planning, rank tracking, or AI-agent workflows.",
|
||||
},
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Search volume", value: "Demand" },
|
||||
{ label: "Keyword difficulty", value: "Competition" },
|
||||
{ label: "CPC", value: "Commercial signal" },
|
||||
{ label: "SERP results", value: "Search context" },
|
||||
],
|
||||
useCases: [
|
||||
"Build a content roadmap from real keyword data.",
|
||||
"Find lower-competition variants before writing.",
|
||||
"Group keywords for articles, landing pages, and rank tracking.",
|
||||
],
|
||||
differentiators: [
|
||||
"Open-source SEO workflows you can self-host or run in the managed app.",
|
||||
"DataForSEO-backed metrics without locking the research process into a black box.",
|
||||
"MCP access so AI agents can research and save keywords for you.",
|
||||
],
|
||||
related: [
|
||||
{ label: "Rank Tracking", href: "/features/rank-tracking" },
|
||||
{ label: "Saved Keywords", href: "/features/saved-keywords" },
|
||||
{ label: "OpenSEO MCP", href: "/features/mcp" },
|
||||
],
|
||||
faqs: [
|
||||
{
|
||||
question: "What is OpenSEO keyword research best for?",
|
||||
answer:
|
||||
"OpenSEO is best for finding SEO keyword ideas, checking demand and difficulty, and turning those ideas into saved keywords you can revisit.",
|
||||
},
|
||||
{
|
||||
question: "Can I use OpenSEO as a free keyword research tool?",
|
||||
answer:
|
||||
"OpenSEO is open source and can be self-hosted. The managed app also keeps keyword research tied to transparent usage instead of a large fixed subscription to a closed SEO suite.",
|
||||
},
|
||||
{
|
||||
question: "Does OpenSEO show live search results?",
|
||||
answer:
|
||||
"Yes. Keyword research can be paired with SERP inspection so you can see ranking pages alongside the metrics.",
|
||||
},
|
||||
],
|
||||
},
|
||||
siteAudit: {
|
||||
slug: FEATURE_PAGE_SLUGS.siteAudit,
|
||||
eyebrow: "Site Audit",
|
||||
navDescription: "Audit page-level SEO signals.",
|
||||
title: "SEO audit tool for finding technical issues fast",
|
||||
description:
|
||||
"Crawl a site, collect page-level technical signals, and optionally run Lighthouse checks for performance, SEO, accessibility, and best-practice issues.",
|
||||
primaryKeyword: "seo audit tool",
|
||||
secondaryKeywords: [
|
||||
"seo site audit",
|
||||
"free seo audit tool",
|
||||
"seo audit tools",
|
||||
],
|
||||
imageAlt: "OpenSEO site audit report",
|
||||
imagePlaceholder: "Site audit screenshot placeholder",
|
||||
imageSrc:
|
||||
"https://imagedelivery.net/ysLOa6bzFaM49Jxok-TAlw/53149e87-0027-4fa8-5d13-bcaab60c7100/public",
|
||||
workflows: [
|
||||
{
|
||||
title: "Run a site crawl",
|
||||
description:
|
||||
"Inspect pages for status codes, titles, meta descriptions, headings, indexability signals, image alt coverage, links, response time, and optional Lighthouse findings.",
|
||||
},
|
||||
{
|
||||
title: "Prioritize issues",
|
||||
description:
|
||||
"Review crawled pages and optional Lighthouse results so the team can focus on visible page and performance problems.",
|
||||
},
|
||||
{
|
||||
title: "Drill into affected URLs",
|
||||
description:
|
||||
"Move into URLs with missing titles, metadata, heading and image-alt signals, status-code issues, response-time data, or optional Lighthouse findings.",
|
||||
},
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Crawled URLs", value: "Coverage" },
|
||||
{ label: "Page fields", value: "Checks" },
|
||||
{ label: "Affected pages", value: "Scope" },
|
||||
{ label: "Audit history", value: "Progress" },
|
||||
],
|
||||
useCases: [
|
||||
"Audit a new site before publishing SEO work.",
|
||||
"Find technical issues after a migration or redesign.",
|
||||
"Export crawled page data and Lighthouse findings for developers and content teams.",
|
||||
],
|
||||
differentiators: [
|
||||
"A practical crawler built into the same workspace as keyword and domain research.",
|
||||
"Open-source implementation for teams that want to inspect or extend the audit flow.",
|
||||
"Simple reports that expose page-level signals and optional Lighthouse findings instead of relying only on a generic score.",
|
||||
],
|
||||
related: [
|
||||
{ label: "Domain Overview", href: "/features/domain-overview" },
|
||||
{ label: "Backlink Checker", href: "/features/backlink-checker" },
|
||||
{ label: "Keyword Research", href: "/features/keyword-research" },
|
||||
],
|
||||
faqs: [
|
||||
{
|
||||
question: "What does the OpenSEO site audit tool check?",
|
||||
answer:
|
||||
"OpenSEO crawls pages, shows page-level technical signals, and can attach Lighthouse issue details when Lighthouse is enabled.",
|
||||
},
|
||||
{
|
||||
question: "Is OpenSEO a free SEO audit tool?",
|
||||
answer:
|
||||
"OpenSEO is open source and can be self-hosted. Managed usage depends on the crawl and data costs behind each workflow.",
|
||||
},
|
||||
{
|
||||
question: "Who should use OpenSEO Site Audit?",
|
||||
answer:
|
||||
"It is useful for founders, marketers, agencies, and developers who need a shared crawl report and optional Lighthouse issue export.",
|
||||
},
|
||||
],
|
||||
},
|
||||
backlinkChecker: {
|
||||
slug: FEATURE_PAGE_SLUGS.backlinkChecker,
|
||||
eyebrow: "Backlinks",
|
||||
navDescription: "Check links and referring domains.",
|
||||
title: "Backlink checker for understanding a domain's link profile",
|
||||
description:
|
||||
"Analyze backlinks, referring domains, and linked pages without separating link research from the rest of your SEO workspace.",
|
||||
primaryKeyword: "backlink checker",
|
||||
secondaryKeywords: [
|
||||
"free backlink checker",
|
||||
"backlink analysis tool",
|
||||
"google backlink checker",
|
||||
],
|
||||
imageAlt: "OpenSEO backlinks report",
|
||||
imagePlaceholder: "Backlink checker screenshot placeholder",
|
||||
imageSrc:
|
||||
"https://imagedelivery.net/ysLOa6bzFaM49Jxok-TAlw/d97206ed-bd64-447c-2b9e-1b9f07c5ec00/public",
|
||||
workflows: [
|
||||
{
|
||||
title: "Check a domain's backlinks",
|
||||
description:
|
||||
"Look up backlinks and referring-domain signals for your site, competitors, or pages you are evaluating.",
|
||||
},
|
||||
{
|
||||
title: "Compare link quality",
|
||||
description:
|
||||
"Use backlink rows, referring-domain rows, rank, spam, broken, lost, and nofollow signals to inspect link quality.",
|
||||
},
|
||||
{
|
||||
title: "Filter and export link data",
|
||||
description:
|
||||
"Export and filter backlink, referring-domain, and top-page data for your own outreach, competitor research, or cleanup review.",
|
||||
},
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Backlinks", value: "Links" },
|
||||
{ label: "Referring domains", value: "Sources" },
|
||||
{ label: "Target URLs", value: "Distribution" },
|
||||
{ label: "Rank and spam signals", value: "Quality context" },
|
||||
],
|
||||
useCases: [
|
||||
"See who links to a competitor.",
|
||||
"Inspect link opportunities for important pages.",
|
||||
"Understand whether a domain has real authority before investing in content.",
|
||||
],
|
||||
differentiators: [
|
||||
"Backlink analysis sits beside keyword research, domain overview, and audit data.",
|
||||
"Self-host or adapt backlink reporting for your team's workflow.",
|
||||
"MCP support lets an AI agent pull backlink context during SEO research.",
|
||||
],
|
||||
related: [
|
||||
{ label: "Domain Overview", href: "/features/domain-overview" },
|
||||
{ label: "Site Audit", href: "/features/site-audit" },
|
||||
{ label: "OpenSEO MCP", href: "/features/mcp" },
|
||||
],
|
||||
faqs: [
|
||||
{
|
||||
question: "What is a backlink checker used for?",
|
||||
answer:
|
||||
"A backlink checker helps you understand which sites link to a domain or page, which links have stronger rank, spam, broken, lost, or nofollow signals, and where competitors are earning authority.",
|
||||
},
|
||||
{
|
||||
question: "Can I check competitor backlinks in OpenSEO?",
|
||||
answer:
|
||||
"Yes. OpenSEO's backlink workflow is designed for researching your own domain as well as competitor domains.",
|
||||
},
|
||||
{
|
||||
question: "How does backlink research connect to SEO planning?",
|
||||
answer:
|
||||
"Backlinks add link-profile context that can inform link-building, digital PR, and competitor research alongside your keyword work.",
|
||||
},
|
||||
],
|
||||
},
|
||||
domainOverview: {
|
||||
slug: FEATURE_PAGE_SLUGS.domainOverview,
|
||||
eyebrow: "Domain Overview",
|
||||
navDescription: "Analyze competitor visibility.",
|
||||
title: "Domain analysis tool for competitor SEO research",
|
||||
description:
|
||||
"Review a domain's estimated organic traffic, organic keyword count, and ranking keyword and page data before deciding where to compete.",
|
||||
primaryKeyword: "domain analysis tool",
|
||||
secondaryKeywords: [
|
||||
"competitor keyword analysis tool",
|
||||
"website traffic checker",
|
||||
"competitor analysis seo tool",
|
||||
],
|
||||
imageAlt: "OpenSEO domain overview",
|
||||
imagePlaceholder: "Domain overview screenshot placeholder",
|
||||
imageSrc:
|
||||
"https://imagedelivery.net/ysLOa6bzFaM49Jxok-TAlw/189e22b8-fdf8-46b4-198c-e912beef2300/public",
|
||||
workflows: [
|
||||
{
|
||||
title: "Analyze a domain",
|
||||
description:
|
||||
"Start with a domain and get an overview of estimated organic traffic, organic keyword count, top ranking keywords, and top organic pages.",
|
||||
},
|
||||
{
|
||||
title: "Find competitor keywords",
|
||||
description:
|
||||
"Inspect keywords a competitor already ranks for and identify topics worth building or defending.",
|
||||
},
|
||||
{
|
||||
title: "Move into deeper research",
|
||||
description:
|
||||
"Use domain insights to open keyword research, backlink analysis, or rank tracking without starting over.",
|
||||
},
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Organic traffic", value: "Visibility" },
|
||||
{ label: "Organic keywords", value: "Topics" },
|
||||
{ label: "Top keywords", value: "Rankings" },
|
||||
{ label: "Top pages", value: "Organic reach" },
|
||||
],
|
||||
useCases: [
|
||||
"Research a competitor before writing a content plan.",
|
||||
"Estimate a site's organic footprint.",
|
||||
"Find keyword gaps between your site and the domains already ranking.",
|
||||
],
|
||||
differentiators: [
|
||||
"Domain research connects directly to keyword, backlink, and rank tracking workflows.",
|
||||
"Built around ranking keywords, estimated traffic, and top pages for practical competitor research.",
|
||||
"Open-source and self-hostable for teams that want control over their SEO stack.",
|
||||
],
|
||||
related: [
|
||||
{ label: "Keyword Research", href: "/features/keyword-research" },
|
||||
{ label: "Backlink Checker", href: "/features/backlink-checker" },
|
||||
{ label: "Rank Tracking", href: "/features/rank-tracking" },
|
||||
],
|
||||
faqs: [
|
||||
{
|
||||
question: "What does a domain analysis tool show?",
|
||||
answer:
|
||||
"It summarizes a domain's organic footprint, including estimated traffic, organic keyword count, ranking keywords, and top organic pages.",
|
||||
},
|
||||
{
|
||||
question: "Can OpenSEO help with competitor keyword analysis?",
|
||||
answer:
|
||||
"Yes. Domain Overview is designed to reveal the keywords and topics a competitor is already visible for.",
|
||||
},
|
||||
{
|
||||
question: "Is Domain Overview the same as a traffic checker?",
|
||||
answer:
|
||||
"It includes traffic-oriented visibility metrics, but the bigger value is connecting that traffic estimate to ranking keywords and top pages.",
|
||||
},
|
||||
],
|
||||
},
|
||||
rankTracking: {
|
||||
slug: FEATURE_PAGE_SLUGS.rankTracking,
|
||||
eyebrow: "Rank Tracking",
|
||||
navDescription: "Monitor keyword positions.",
|
||||
title: "Rank tracker for monitoring keyword positions",
|
||||
description:
|
||||
"Track the keywords that matter, optionally compare desktop and mobile results, and keep ranking changes connected to your research workflow.",
|
||||
primaryKeyword: "rank tracker",
|
||||
secondaryKeywords: [
|
||||
"seo rank tracking tool",
|
||||
"keyword rank tracker",
|
||||
"google rank tracker",
|
||||
],
|
||||
imageAlt: "OpenSEO rank tracking table",
|
||||
imagePlaceholder: "Rank tracking screenshot placeholder",
|
||||
imageSrc:
|
||||
"https://imagedelivery.net/ysLOa6bzFaM49Jxok-TAlw/4a0f8508-1527-46a8-c91c-086456f21c00/public",
|
||||
workflows: [
|
||||
{
|
||||
title: "Add tracked domains",
|
||||
description:
|
||||
"Create rank tracking configurations for the domains and locations you care about.",
|
||||
},
|
||||
{
|
||||
title: "Track important keywords",
|
||||
description:
|
||||
"Add keywords manually or from ranking suggestions and monitor positions over time.",
|
||||
},
|
||||
{
|
||||
title: "Compare SERP context",
|
||||
description:
|
||||
"Review the configured device results, ranking URLs, movement, and available SERP feature signals.",
|
||||
},
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Desktop rank", value: "When enabled" },
|
||||
{ label: "Mobile rank", value: "When enabled" },
|
||||
{ label: "SERP features", value: "Context" },
|
||||
{ label: "Position change", value: "Movement" },
|
||||
],
|
||||
useCases: [
|
||||
"Monitor target keywords after publishing content.",
|
||||
"Track launch, migration, and optimization impact.",
|
||||
"Keep ranking checks close to the keywords your team already researched.",
|
||||
],
|
||||
differentiators: [
|
||||
"Rank tracking is part of the same workspace as discovery, audit, and competitor research.",
|
||||
"Optional desktop and mobile tracking helps teams avoid one-dimensional rank reports.",
|
||||
"OpenSEO can expose ranking data to AI agents through MCP.",
|
||||
],
|
||||
related: [
|
||||
{ label: "Keyword Research", href: "/features/keyword-research" },
|
||||
{ label: "Saved Keywords", href: "/features/saved-keywords" },
|
||||
{ label: "OpenSEO MCP", href: "/features/mcp" },
|
||||
],
|
||||
faqs: [
|
||||
{
|
||||
question: "What is a rank tracker?",
|
||||
answer:
|
||||
"A rank tracker monitors where a domain appears for selected keywords over time so you can see whether SEO work is improving visibility.",
|
||||
},
|
||||
{
|
||||
question: "Does OpenSEO track mobile and desktop rankings?",
|
||||
answer:
|
||||
"OpenSEO rank tracking can be configured for mobile, desktop, or both, so teams can compare devices when both are enabled.",
|
||||
},
|
||||
{
|
||||
question: "How should I choose keywords to track?",
|
||||
answer:
|
||||
"Start with keywords tied to important pages, active content work, and competitor opportunities discovered in keyword research.",
|
||||
},
|
||||
],
|
||||
},
|
||||
savedKeywords: {
|
||||
slug: FEATURE_PAGE_SLUGS.savedKeywords,
|
||||
eyebrow: "Saved Keywords",
|
||||
navDescription: "Organize SEO opportunities.",
|
||||
title: "Saved keywords for turning SEO research into a plan",
|
||||
description:
|
||||
"Keep useful keyword ideas organized so they can inform content planning, rank tracking decisions, and AI-agent workflows.",
|
||||
primaryKeyword: "saved keywords",
|
||||
secondaryKeywords: [
|
||||
"seo keyword list",
|
||||
"keyword list tool",
|
||||
"keyword planning",
|
||||
],
|
||||
imageAlt: "OpenSEO saved keywords list",
|
||||
imagePlaceholder: "Saved keywords screenshot placeholder",
|
||||
imageSrc:
|
||||
"https://imagedelivery.net/ysLOa6bzFaM49Jxok-TAlw/8938a529-b443-4d4f-9869-c972f3cef900/public",
|
||||
workflows: [
|
||||
{
|
||||
title: "Save promising keywords",
|
||||
description:
|
||||
"Collect useful ideas from keyword research instead of losing them after each search.",
|
||||
},
|
||||
{
|
||||
title: "Organize by topic",
|
||||
description:
|
||||
"Tag keywords by page, campaign, content cluster, or priority so planning stays readable.",
|
||||
},
|
||||
{
|
||||
title: "Reuse saved keywords across workflows",
|
||||
description:
|
||||
"Use saved keywords and tags as a planning reference for rank tracking, content planning, or MCP-powered research.",
|
||||
},
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Saved ideas", value: "Pipeline" },
|
||||
{ label: "Tags", value: "Organization" },
|
||||
{ label: "Volume", value: "Demand" },
|
||||
{ label: "Difficulty", value: "Priority" },
|
||||
],
|
||||
useCases: [
|
||||
"Tag keyword ideas into topic or page groups from keyword research.",
|
||||
"Prepare candidate keywords to add to rank tracking.",
|
||||
"Keep human and AI-agent research in the same workspace.",
|
||||
],
|
||||
differentiators: [
|
||||
"Saved keywords bridge research, tracking, and AI workflows.",
|
||||
"Saved keywords preserve available metrics like volume, CPC, difficulty, intent, and tags.",
|
||||
"The workflow stays simple enough for repeated planning sessions.",
|
||||
],
|
||||
related: [
|
||||
{ label: "Keyword Research", href: "/features/keyword-research" },
|
||||
{ label: "Rank Tracking", href: "/features/rank-tracking" },
|
||||
{ label: "OpenSEO MCP", href: "/features/mcp" },
|
||||
],
|
||||
faqs: [
|
||||
{
|
||||
question: "Why save keywords in an SEO tool?",
|
||||
answer:
|
||||
"Saved keywords keep research organized so teams can return to the ideas that are worth writing, optimizing, or tracking.",
|
||||
},
|
||||
{
|
||||
question: "Can saved keywords be used with rank tracking?",
|
||||
answer:
|
||||
"Yes. Saved keywords are a natural source for deciding which terms should be monitored over time.",
|
||||
},
|
||||
{
|
||||
question: "How do saved keywords fit into SEO planning?",
|
||||
answer:
|
||||
"Saved Keywords keeps promising ideas organized so they can inform content planning, rank tracking decisions, and future research.",
|
||||
},
|
||||
],
|
||||
},
|
||||
aiBrandVisibility: {
|
||||
slug: FEATURE_PAGE_SLUGS.aiBrandVisibility,
|
||||
eyebrow: "AI Visibility",
|
||||
navDescription: "Look up brand mentions in AI search.",
|
||||
title: "Brand lookup for ChatGPT and Google AI Overview visibility",
|
||||
description:
|
||||
"Look up a brand or domain and review ChatGPT and Google AI Overview mentions, cited pages, and related prompts.",
|
||||
primaryKeyword: "ai visibility tool",
|
||||
secondaryKeywords: [
|
||||
"brand visibility ai search",
|
||||
"ai search visibility",
|
||||
"answer engine optimization",
|
||||
],
|
||||
imageAlt: "OpenSEO AI brand visibility report",
|
||||
imagePlaceholder: "AI brand visibility screenshot placeholder",
|
||||
imageSrc:
|
||||
"https://imagedelivery.net/ysLOa6bzFaM49Jxok-TAlw/cde3e4f8-079f-4890-cb17-371087107400/public",
|
||||
workflows: [
|
||||
{
|
||||
title: "Look up a brand",
|
||||
description:
|
||||
"Search for a brand or domain and inspect how ChatGPT and Google AI Overview mention or cite it in available results.",
|
||||
},
|
||||
{
|
||||
title: "Review citations and platforms",
|
||||
description:
|
||||
"Review the URLs, domains, and platforms contributing to brand mentions.",
|
||||
},
|
||||
{
|
||||
title: "Find visibility gaps",
|
||||
description:
|
||||
"Use cited pages and related prompts as clues for content, reputation, or comparison coverage to investigate.",
|
||||
},
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Mentions", value: "Presence" },
|
||||
{ label: "Citations", value: "Sources" },
|
||||
{ label: "Platforms", value: "Surfaces" },
|
||||
{ label: "Cited domains", value: "Sources" },
|
||||
],
|
||||
useCases: [
|
||||
"See whether ChatGPT and Google AI Overview data mention or cite your brand or domain.",
|
||||
"Find pages and domains cited alongside brand mentions.",
|
||||
"Use cited sources and prompts to plan content experiments for answer-engine visibility.",
|
||||
],
|
||||
differentiators: [
|
||||
"AI visibility sits beside classic SEO research instead of replacing it.",
|
||||
"The workflow focuses on concrete sources and mentions, not vague AI hype.",
|
||||
"OpenSEO helps teams connect AI mention and citation research to concrete SEO planning.",
|
||||
],
|
||||
related: [
|
||||
{ label: "AI Search Prompts", href: "/features/ai-search-prompts" },
|
||||
{ label: "Domain Overview", href: "/features/domain-overview" },
|
||||
{ label: "OpenSEO MCP", href: "/features/mcp" },
|
||||
],
|
||||
faqs: [
|
||||
{
|
||||
question: "What is AI brand visibility?",
|
||||
answer:
|
||||
"AI brand visibility is how often your brand or domain appears in available ChatGPT and Google AI Overview mention and citation data.",
|
||||
},
|
||||
{
|
||||
question: "How is AI visibility different from traditional SEO?",
|
||||
answer:
|
||||
"Traditional SEO focuses on rankings and pages. OpenSEO's AI visibility workflow looks at mentions, cited pages, related prompts, and platform-level metrics from supported AI-search sources.",
|
||||
},
|
||||
{
|
||||
question: "Should AI visibility replace keyword research?",
|
||||
answer:
|
||||
"No. It should sit beside keyword, domain, backlink, and audit data so teams can understand both search rankings and answer coverage.",
|
||||
},
|
||||
],
|
||||
},
|
||||
aiSearchPrompts: {
|
||||
slug: FEATURE_PAGE_SLUGS.aiSearchPrompts,
|
||||
eyebrow: "Prompt Explorer",
|
||||
navDescription: "Compare answers across supported models.",
|
||||
title: "AI search prompt explorer for visibility research",
|
||||
description:
|
||||
"Run the same prompt across supported AI models, compare the answers, and review citations when they are returned.",
|
||||
primaryKeyword: "ai search visibility",
|
||||
secondaryKeywords: [
|
||||
"chatgpt search visibility",
|
||||
"ai search prompts",
|
||||
"answer engine optimization tool",
|
||||
],
|
||||
imageAlt: "OpenSEO prompt explorer",
|
||||
imagePlaceholder: "Prompt explorer screenshot placeholder",
|
||||
imageSrc:
|
||||
"https://imagedelivery.net/ysLOa6bzFaM49Jxok-TAlw/9f3d38f2-aa97-417c-ca74-ae378654d700/public",
|
||||
workflows: [
|
||||
{
|
||||
title: "Test category prompts",
|
||||
description:
|
||||
"Compare answers to the questions your customers might ask AI tools.",
|
||||
},
|
||||
{
|
||||
title: "Inspect web-backed answers",
|
||||
description:
|
||||
"When web search is enabled, inspect the pages and domains cited by model responses.",
|
||||
},
|
||||
{
|
||||
title: "Check brand mentions",
|
||||
description:
|
||||
"Highlight a brand and see whether each model mentions it in the answer or cited sources.",
|
||||
},
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Prompts", value: "Questions" },
|
||||
{ label: "Web context", value: "Sources" },
|
||||
{ label: "Web search country", value: "Regional context" },
|
||||
{ label: "Brand mentions", value: "Presence" },
|
||||
],
|
||||
useCases: [
|
||||
"Compare how supported AI models answer the same prompt.",
|
||||
"Review which pages and domains appear in cited sources.",
|
||||
"Check whether a brand appears in AI answers and citations.",
|
||||
],
|
||||
differentiators: [
|
||||
"Prompt research lives in the same workspace as domain, keyword, and brand visibility workflows.",
|
||||
"OpenSEO treats AI search as a research layer, not a replacement for SEO fundamentals.",
|
||||
"OpenSEO MCP exposes keyword, SERP, domain, backlink, saved keyword, and rank-tracking tools to AI agents.",
|
||||
],
|
||||
related: [
|
||||
{ label: "AI Brand Visibility", href: "/features/ai-brand-visibility" },
|
||||
{ label: "Keyword Research", href: "/features/keyword-research" },
|
||||
{ label: "OpenSEO MCP", href: "/features/mcp" },
|
||||
],
|
||||
faqs: [
|
||||
{
|
||||
question: "What is an AI search prompt explorer?",
|
||||
answer:
|
||||
"It lets teams run the same prompt across supported AI models, compare answers, and inspect citation URLs returned with supported model responses.",
|
||||
},
|
||||
{
|
||||
question: "Why does prompt research matter for SEO?",
|
||||
answer:
|
||||
"Prompts reveal comparison, problem, and buying questions that can inform pages, guides, and the pages or domains that appear in returned citations.",
|
||||
},
|
||||
{
|
||||
question: "Can this help with answer engine optimization?",
|
||||
answer:
|
||||
"Yes. Prompt Explorer is a starting point for mapping prompt responses and returned citations back to source pages and possible SEO follow-up work.",
|
||||
},
|
||||
],
|
||||
},
|
||||
} satisfies Record<string, FeaturePage>;
|
||||
|
||||
export const featureGroups = [
|
||||
{
|
||||
label: "Keyword workflows",
|
||||
description: "Find, organize, and monitor the keywords that matter.",
|
||||
pages: [
|
||||
featurePages.keywordResearch,
|
||||
featurePages.savedKeywords,
|
||||
featurePages.rankTracking,
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Domain research",
|
||||
description: "Understand competitors, backlinks, and technical health.",
|
||||
pages: [
|
||||
featurePages.domainOverview,
|
||||
featurePages.backlinkChecker,
|
||||
featurePages.siteAudit,
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "AI visibility",
|
||||
description: "Research AI search prompts, citations, and brand visibility.",
|
||||
pages: [featurePages.aiBrandVisibility, featurePages.aiSearchPrompts],
|
||||
},
|
||||
] as const;
|
||||
@ -19,7 +19,16 @@ import { Route as GuidesSplatRouteImport } from './routes/guides/$'
|
||||
import { Route as ApiSubscribeRouteImport } from './routes/api/subscribe'
|
||||
import { Route as ApiEventRouteImport } from './routes/api/event'
|
||||
import { Route as MarketingPricingRouteImport } from './routes/_marketing/pricing'
|
||||
import { Route as MarketingFeaturesIndexRouteImport } from './routes/_marketing/features/index'
|
||||
import { Route as MarketingFeaturesSiteAuditRouteImport } from './routes/_marketing/features/site-audit'
|
||||
import { Route as MarketingFeaturesSavedKeywordsRouteImport } from './routes/_marketing/features/saved-keywords'
|
||||
import { Route as MarketingFeaturesRankTrackingRouteImport } from './routes/_marketing/features/rank-tracking'
|
||||
import { Route as MarketingFeaturesMcpRouteImport } from './routes/_marketing/features/mcp'
|
||||
import { Route as MarketingFeaturesKeywordResearchRouteImport } from './routes/_marketing/features/keyword-research'
|
||||
import { Route as MarketingFeaturesDomainOverviewRouteImport } from './routes/_marketing/features/domain-overview'
|
||||
import { Route as MarketingFeaturesBacklinkCheckerRouteImport } from './routes/_marketing/features/backlink-checker'
|
||||
import { Route as MarketingFeaturesAiSearchPromptsRouteImport } from './routes/_marketing/features/ai-search-prompts'
|
||||
import { Route as MarketingFeaturesAiBrandVisibilityRouteImport } from './routes/_marketing/features/ai-brand-visibility'
|
||||
|
||||
const TermsAndConditionsRoute = TermsAndConditionsRouteImport.update({
|
||||
id: '/terms-and-conditions',
|
||||
@ -70,11 +79,64 @@ const MarketingPricingRoute = MarketingPricingRouteImport.update({
|
||||
path: '/pricing',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesIndexRoute = MarketingFeaturesIndexRouteImport.update({
|
||||
id: '/features/',
|
||||
path: '/features/',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesSiteAuditRoute =
|
||||
MarketingFeaturesSiteAuditRouteImport.update({
|
||||
id: '/features/site-audit',
|
||||
path: '/features/site-audit',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesSavedKeywordsRoute =
|
||||
MarketingFeaturesSavedKeywordsRouteImport.update({
|
||||
id: '/features/saved-keywords',
|
||||
path: '/features/saved-keywords',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesRankTrackingRoute =
|
||||
MarketingFeaturesRankTrackingRouteImport.update({
|
||||
id: '/features/rank-tracking',
|
||||
path: '/features/rank-tracking',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesMcpRoute = MarketingFeaturesMcpRouteImport.update({
|
||||
id: '/features/mcp',
|
||||
path: '/features/mcp',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesKeywordResearchRoute =
|
||||
MarketingFeaturesKeywordResearchRouteImport.update({
|
||||
id: '/features/keyword-research',
|
||||
path: '/features/keyword-research',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesDomainOverviewRoute =
|
||||
MarketingFeaturesDomainOverviewRouteImport.update({
|
||||
id: '/features/domain-overview',
|
||||
path: '/features/domain-overview',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesBacklinkCheckerRoute =
|
||||
MarketingFeaturesBacklinkCheckerRouteImport.update({
|
||||
id: '/features/backlink-checker',
|
||||
path: '/features/backlink-checker',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesAiSearchPromptsRoute =
|
||||
MarketingFeaturesAiSearchPromptsRouteImport.update({
|
||||
id: '/features/ai-search-prompts',
|
||||
path: '/features/ai-search-prompts',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
const MarketingFeaturesAiBrandVisibilityRoute =
|
||||
MarketingFeaturesAiBrandVisibilityRouteImport.update({
|
||||
id: '/features/ai-brand-visibility',
|
||||
path: '/features/ai-brand-visibility',
|
||||
getParentRoute: () => MarketingRoute,
|
||||
} as any)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof MarketingIndexRoute
|
||||
@ -86,7 +148,16 @@ export interface FileRoutesByFullPath {
|
||||
'/guides/$': typeof GuidesSplatRoute
|
||||
'/js/script.js': typeof JsScriptDotjsRoute
|
||||
'/guides/': typeof GuidesIndexRoute
|
||||
'/features/ai-brand-visibility': typeof MarketingFeaturesAiBrandVisibilityRoute
|
||||
'/features/ai-search-prompts': typeof MarketingFeaturesAiSearchPromptsRoute
|
||||
'/features/backlink-checker': typeof MarketingFeaturesBacklinkCheckerRoute
|
||||
'/features/domain-overview': typeof MarketingFeaturesDomainOverviewRoute
|
||||
'/features/keyword-research': typeof MarketingFeaturesKeywordResearchRoute
|
||||
'/features/mcp': typeof MarketingFeaturesMcpRoute
|
||||
'/features/rank-tracking': typeof MarketingFeaturesRankTrackingRoute
|
||||
'/features/saved-keywords': typeof MarketingFeaturesSavedKeywordsRoute
|
||||
'/features/site-audit': typeof MarketingFeaturesSiteAuditRoute
|
||||
'/features/': typeof MarketingFeaturesIndexRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/privacy': typeof PrivacyRoute
|
||||
@ -98,7 +169,16 @@ export interface FileRoutesByTo {
|
||||
'/js/script.js': typeof JsScriptDotjsRoute
|
||||
'/': typeof MarketingIndexRoute
|
||||
'/guides': typeof GuidesIndexRoute
|
||||
'/features/ai-brand-visibility': typeof MarketingFeaturesAiBrandVisibilityRoute
|
||||
'/features/ai-search-prompts': typeof MarketingFeaturesAiSearchPromptsRoute
|
||||
'/features/backlink-checker': typeof MarketingFeaturesBacklinkCheckerRoute
|
||||
'/features/domain-overview': typeof MarketingFeaturesDomainOverviewRoute
|
||||
'/features/keyword-research': typeof MarketingFeaturesKeywordResearchRoute
|
||||
'/features/mcp': typeof MarketingFeaturesMcpRoute
|
||||
'/features/rank-tracking': typeof MarketingFeaturesRankTrackingRoute
|
||||
'/features/saved-keywords': typeof MarketingFeaturesSavedKeywordsRoute
|
||||
'/features/site-audit': typeof MarketingFeaturesSiteAuditRoute
|
||||
'/features': typeof MarketingFeaturesIndexRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
@ -112,7 +192,16 @@ export interface FileRoutesById {
|
||||
'/js/script.js': typeof JsScriptDotjsRoute
|
||||
'/_marketing/': typeof MarketingIndexRoute
|
||||
'/guides/': typeof GuidesIndexRoute
|
||||
'/_marketing/features/ai-brand-visibility': typeof MarketingFeaturesAiBrandVisibilityRoute
|
||||
'/_marketing/features/ai-search-prompts': typeof MarketingFeaturesAiSearchPromptsRoute
|
||||
'/_marketing/features/backlink-checker': typeof MarketingFeaturesBacklinkCheckerRoute
|
||||
'/_marketing/features/domain-overview': typeof MarketingFeaturesDomainOverviewRoute
|
||||
'/_marketing/features/keyword-research': typeof MarketingFeaturesKeywordResearchRoute
|
||||
'/_marketing/features/mcp': typeof MarketingFeaturesMcpRoute
|
||||
'/_marketing/features/rank-tracking': typeof MarketingFeaturesRankTrackingRoute
|
||||
'/_marketing/features/saved-keywords': typeof MarketingFeaturesSavedKeywordsRoute
|
||||
'/_marketing/features/site-audit': typeof MarketingFeaturesSiteAuditRoute
|
||||
'/_marketing/features/': typeof MarketingFeaturesIndexRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
@ -126,7 +215,16 @@ export interface FileRouteTypes {
|
||||
| '/guides/$'
|
||||
| '/js/script.js'
|
||||
| '/guides/'
|
||||
| '/features/ai-brand-visibility'
|
||||
| '/features/ai-search-prompts'
|
||||
| '/features/backlink-checker'
|
||||
| '/features/domain-overview'
|
||||
| '/features/keyword-research'
|
||||
| '/features/mcp'
|
||||
| '/features/rank-tracking'
|
||||
| '/features/saved-keywords'
|
||||
| '/features/site-audit'
|
||||
| '/features/'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/privacy'
|
||||
@ -138,7 +236,16 @@ export interface FileRouteTypes {
|
||||
| '/js/script.js'
|
||||
| '/'
|
||||
| '/guides'
|
||||
| '/features/ai-brand-visibility'
|
||||
| '/features/ai-search-prompts'
|
||||
| '/features/backlink-checker'
|
||||
| '/features/domain-overview'
|
||||
| '/features/keyword-research'
|
||||
| '/features/mcp'
|
||||
| '/features/rank-tracking'
|
||||
| '/features/saved-keywords'
|
||||
| '/features/site-audit'
|
||||
| '/features'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/_marketing'
|
||||
@ -151,7 +258,16 @@ export interface FileRouteTypes {
|
||||
| '/js/script.js'
|
||||
| '/_marketing/'
|
||||
| '/guides/'
|
||||
| '/_marketing/features/ai-brand-visibility'
|
||||
| '/_marketing/features/ai-search-prompts'
|
||||
| '/_marketing/features/backlink-checker'
|
||||
| '/_marketing/features/domain-overview'
|
||||
| '/_marketing/features/keyword-research'
|
||||
| '/_marketing/features/mcp'
|
||||
| '/_marketing/features/rank-tracking'
|
||||
| '/_marketing/features/saved-keywords'
|
||||
| '/_marketing/features/site-audit'
|
||||
| '/_marketing/features/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
@ -237,6 +353,34 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof MarketingPricingRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/': {
|
||||
id: '/_marketing/features/'
|
||||
path: '/features'
|
||||
fullPath: '/features/'
|
||||
preLoaderRoute: typeof MarketingFeaturesIndexRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/site-audit': {
|
||||
id: '/_marketing/features/site-audit'
|
||||
path: '/features/site-audit'
|
||||
fullPath: '/features/site-audit'
|
||||
preLoaderRoute: typeof MarketingFeaturesSiteAuditRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/saved-keywords': {
|
||||
id: '/_marketing/features/saved-keywords'
|
||||
path: '/features/saved-keywords'
|
||||
fullPath: '/features/saved-keywords'
|
||||
preLoaderRoute: typeof MarketingFeaturesSavedKeywordsRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/rank-tracking': {
|
||||
id: '/_marketing/features/rank-tracking'
|
||||
path: '/features/rank-tracking'
|
||||
fullPath: '/features/rank-tracking'
|
||||
preLoaderRoute: typeof MarketingFeaturesRankTrackingRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/mcp': {
|
||||
id: '/_marketing/features/mcp'
|
||||
path: '/features/mcp'
|
||||
@ -244,19 +388,73 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof MarketingFeaturesMcpRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/keyword-research': {
|
||||
id: '/_marketing/features/keyword-research'
|
||||
path: '/features/keyword-research'
|
||||
fullPath: '/features/keyword-research'
|
||||
preLoaderRoute: typeof MarketingFeaturesKeywordResearchRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/domain-overview': {
|
||||
id: '/_marketing/features/domain-overview'
|
||||
path: '/features/domain-overview'
|
||||
fullPath: '/features/domain-overview'
|
||||
preLoaderRoute: typeof MarketingFeaturesDomainOverviewRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/backlink-checker': {
|
||||
id: '/_marketing/features/backlink-checker'
|
||||
path: '/features/backlink-checker'
|
||||
fullPath: '/features/backlink-checker'
|
||||
preLoaderRoute: typeof MarketingFeaturesBacklinkCheckerRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/ai-search-prompts': {
|
||||
id: '/_marketing/features/ai-search-prompts'
|
||||
path: '/features/ai-search-prompts'
|
||||
fullPath: '/features/ai-search-prompts'
|
||||
preLoaderRoute: typeof MarketingFeaturesAiSearchPromptsRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
'/_marketing/features/ai-brand-visibility': {
|
||||
id: '/_marketing/features/ai-brand-visibility'
|
||||
path: '/features/ai-brand-visibility'
|
||||
fullPath: '/features/ai-brand-visibility'
|
||||
preLoaderRoute: typeof MarketingFeaturesAiBrandVisibilityRouteImport
|
||||
parentRoute: typeof MarketingRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface MarketingRouteChildren {
|
||||
MarketingPricingRoute: typeof MarketingPricingRoute
|
||||
MarketingIndexRoute: typeof MarketingIndexRoute
|
||||
MarketingFeaturesAiBrandVisibilityRoute: typeof MarketingFeaturesAiBrandVisibilityRoute
|
||||
MarketingFeaturesAiSearchPromptsRoute: typeof MarketingFeaturesAiSearchPromptsRoute
|
||||
MarketingFeaturesBacklinkCheckerRoute: typeof MarketingFeaturesBacklinkCheckerRoute
|
||||
MarketingFeaturesDomainOverviewRoute: typeof MarketingFeaturesDomainOverviewRoute
|
||||
MarketingFeaturesKeywordResearchRoute: typeof MarketingFeaturesKeywordResearchRoute
|
||||
MarketingFeaturesMcpRoute: typeof MarketingFeaturesMcpRoute
|
||||
MarketingFeaturesRankTrackingRoute: typeof MarketingFeaturesRankTrackingRoute
|
||||
MarketingFeaturesSavedKeywordsRoute: typeof MarketingFeaturesSavedKeywordsRoute
|
||||
MarketingFeaturesSiteAuditRoute: typeof MarketingFeaturesSiteAuditRoute
|
||||
MarketingFeaturesIndexRoute: typeof MarketingFeaturesIndexRoute
|
||||
}
|
||||
|
||||
const MarketingRouteChildren: MarketingRouteChildren = {
|
||||
MarketingPricingRoute: MarketingPricingRoute,
|
||||
MarketingIndexRoute: MarketingIndexRoute,
|
||||
MarketingFeaturesAiBrandVisibilityRoute:
|
||||
MarketingFeaturesAiBrandVisibilityRoute,
|
||||
MarketingFeaturesAiSearchPromptsRoute: MarketingFeaturesAiSearchPromptsRoute,
|
||||
MarketingFeaturesBacklinkCheckerRoute: MarketingFeaturesBacklinkCheckerRoute,
|
||||
MarketingFeaturesDomainOverviewRoute: MarketingFeaturesDomainOverviewRoute,
|
||||
MarketingFeaturesKeywordResearchRoute: MarketingFeaturesKeywordResearchRoute,
|
||||
MarketingFeaturesMcpRoute: MarketingFeaturesMcpRoute,
|
||||
MarketingFeaturesRankTrackingRoute: MarketingFeaturesRankTrackingRoute,
|
||||
MarketingFeaturesSavedKeywordsRoute: MarketingFeaturesSavedKeywordsRoute,
|
||||
MarketingFeaturesSiteAuditRoute: MarketingFeaturesSiteAuditRoute,
|
||||
MarketingFeaturesIndexRoute: MarketingFeaturesIndexRoute,
|
||||
}
|
||||
|
||||
const MarketingRouteWithChildren = MarketingRoute._addFileChildren(
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { createFileRoute, Link, Outlet } from "@tanstack/react-router";
|
||||
import { useState } from "react";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import { featureGroups } from "@/lib/feature-pages";
|
||||
|
||||
function GitHubIcon({ size = 18 }: { size?: number }) {
|
||||
return (
|
||||
@ -31,12 +32,7 @@ function MarketingLayout() {
|
||||
OpenSEO
|
||||
</Link>
|
||||
<div className="flex items-center gap-4 sm:gap-5">
|
||||
<Link
|
||||
to="/guides"
|
||||
className="text-sm text-neutral-600 hover:text-neutral-900 transition-colors"
|
||||
>
|
||||
Guides
|
||||
</Link>
|
||||
<FeatureDropdown />
|
||||
<Link
|
||||
to="/pricing"
|
||||
className="text-sm text-neutral-600 hover:text-neutral-900 transition-colors"
|
||||
@ -68,6 +64,78 @@ function MarketingLayout() {
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureDropdown() {
|
||||
return (
|
||||
<div className="relative group">
|
||||
<Link
|
||||
to="/features"
|
||||
className="text-sm text-neutral-600 transition-colors hover:text-neutral-900 md:hidden"
|
||||
>
|
||||
Features
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="hidden text-sm text-neutral-600 transition-colors hover:text-neutral-900 md:inline-flex"
|
||||
>
|
||||
Features
|
||||
</button>
|
||||
<div className="pointer-events-none absolute left-1/2 top-full z-20 hidden w-[560px] -translate-x-1/2 pt-4 opacity-0 transition md:block group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100">
|
||||
<div className="rounded-lg border border-neutral-200 bg-white p-5 shadow-xl shadow-neutral-900/10">
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-6">
|
||||
{featureGroups.map((group) => (
|
||||
<div key={group.label}>
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-neutral-500">
|
||||
{group.label}
|
||||
</p>
|
||||
<div className="mt-3 space-y-1">
|
||||
{group.pages.map((page) => (
|
||||
<a
|
||||
key={page.slug}
|
||||
href={`/features/${page.slug}`}
|
||||
className="block rounded-md px-2 py-1.5 transition-colors hover:bg-neutral-50"
|
||||
>
|
||||
<span className="block text-sm font-semibold text-neutral-900">
|
||||
{page.eyebrow}
|
||||
</span>
|
||||
<span className="mt-0.5 block text-xs leading-relaxed text-neutral-600">
|
||||
{page.navDescription}
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-neutral-500">
|
||||
AI agents
|
||||
</p>
|
||||
<div className="mt-3 space-y-2">
|
||||
<a
|
||||
href="/features/mcp"
|
||||
className="block rounded-md p-2 transition-colors hover:bg-neutral-50"
|
||||
>
|
||||
<span className="text-sm font-semibold text-neutral-900">
|
||||
OpenSEO MCP
|
||||
</span>
|
||||
<span className="mt-0.5 block text-xs leading-relaxed text-neutral-600">
|
||||
Connect Claude, Codex, and agents.
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href="/features"
|
||||
className="block rounded-md border border-neutral-200 bg-neutral-50 px-2 py-1.5 text-sm font-medium text-neutral-900 transition-colors hover:border-neutral-900"
|
||||
>
|
||||
View all features <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function useNewsletter() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [status, setStatus] = useState<
|
||||
|
||||
18
web/src/routes/_marketing/features/ai-brand-visibility.tsx
Normal file
18
web/src/routes/_marketing/features/ai-brand-visibility.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { FeaturePageTemplate } from "@/components/feature-page";
|
||||
import { featurePages } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const page = featurePages.aiBrandVisibility;
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/ai-brand-visibility")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "AI Brand Visibility Tool",
|
||||
description: page.description,
|
||||
path: "/features/ai-brand-visibility",
|
||||
titleSuffix: "OpenSEO",
|
||||
imageAlt: page.imageAlt,
|
||||
}),
|
||||
component: () => <FeaturePageTemplate page={page} />,
|
||||
});
|
||||
18
web/src/routes/_marketing/features/ai-search-prompts.tsx
Normal file
18
web/src/routes/_marketing/features/ai-search-prompts.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { FeaturePageTemplate } from "@/components/feature-page";
|
||||
import { featurePages } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const page = featurePages.aiSearchPrompts;
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/ai-search-prompts")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "AI Search Prompt Explorer",
|
||||
description: page.description,
|
||||
path: "/features/ai-search-prompts",
|
||||
titleSuffix: "OpenSEO",
|
||||
imageAlt: page.imageAlt,
|
||||
}),
|
||||
component: () => <FeaturePageTemplate page={page} />,
|
||||
});
|
||||
18
web/src/routes/_marketing/features/backlink-checker.tsx
Normal file
18
web/src/routes/_marketing/features/backlink-checker.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { FeaturePageTemplate } from "@/components/feature-page";
|
||||
import { featurePages } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const page = featurePages.backlinkChecker;
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/backlink-checker")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "Backlink Checker",
|
||||
description: page.description,
|
||||
path: "/features/backlink-checker",
|
||||
titleSuffix: "OpenSEO",
|
||||
imageAlt: page.imageAlt,
|
||||
}),
|
||||
component: () => <FeaturePageTemplate page={page} />,
|
||||
});
|
||||
18
web/src/routes/_marketing/features/domain-overview.tsx
Normal file
18
web/src/routes/_marketing/features/domain-overview.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { FeaturePageTemplate } from "@/components/feature-page";
|
||||
import { featurePages } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const page = featurePages.domainOverview;
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/domain-overview")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "Domain Analysis Tool",
|
||||
description: page.description,
|
||||
path: "/features/domain-overview",
|
||||
titleSuffix: "OpenSEO",
|
||||
imageAlt: page.imageAlt,
|
||||
}),
|
||||
component: () => <FeaturePageTemplate page={page} />,
|
||||
});
|
||||
100
web/src/routes/_marketing/features/index.tsx
Normal file
100
web/src/routes/_marketing/features/index.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { featureGroups } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const featuresDescription =
|
||||
"Explore OpenSEO's open-source SEO tools for AI-agent workflows, keyword research, rank tracking, backlinks, site audits, competitor analysis, and AI visibility.";
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "OpenSEO Features",
|
||||
description: featuresDescription,
|
||||
path: "/features",
|
||||
titleSuffix: "OpenSEO",
|
||||
}),
|
||||
component: FeaturesIndex,
|
||||
});
|
||||
|
||||
function FeaturesIndex() {
|
||||
return (
|
||||
<>
|
||||
<p className="text-sm font-medium text-neutral-500">
|
||||
Open-source SEO tools
|
||||
</p>
|
||||
<h1 className="mt-3 text-3xl font-bold leading-tight tracking-tight">
|
||||
A delightful suite of SEO tools
|
||||
</h1>
|
||||
<p className="mt-4 leading-relaxed text-neutral-700">
|
||||
Research keywords, track rankings, audit sites, and understand your AI
|
||||
visibility from one modern platform.
|
||||
</p>
|
||||
|
||||
<div className="mt-10 space-y-12">
|
||||
<section>
|
||||
<div className="border-b border-neutral-200 pb-3">
|
||||
<h2 className="text-xl font-semibold text-neutral-900">
|
||||
AI agent workflows
|
||||
</h2>
|
||||
<p className="mt-1 text-sm leading-relaxed text-neutral-600">
|
||||
Let supported MCP clients research keywords, SERPs, domains, and
|
||||
backlinks through OpenSEO.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/features/mcp"
|
||||
className="mt-5 block rounded-lg border border-neutral-200 bg-white p-5 transition-colors hover:border-neutral-900"
|
||||
>
|
||||
<p className="text-xs font-medium text-neutral-500">OpenSEO MCP</p>
|
||||
<h3 className="mt-2 text-lg font-semibold text-neutral-900">
|
||||
OpenSEO MCP for your AI agent
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-neutral-600">
|
||||
Connect OpenSEO to Claude, Codex, and supported MCP clients so
|
||||
agents can call OpenSEO research tools with authorized project
|
||||
context.
|
||||
</p>
|
||||
<p className="mt-3 text-sm font-medium text-neutral-900">
|
||||
Explore MCP <span aria-hidden="true">→</span>
|
||||
</p>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
{featureGroups.map((group) => (
|
||||
<section key={group.label}>
|
||||
<div className="border-b border-neutral-200 pb-3">
|
||||
<h2 className="text-xl font-semibold text-neutral-900">
|
||||
{group.label}
|
||||
</h2>
|
||||
<p className="mt-1 text-sm leading-relaxed text-neutral-600">
|
||||
{group.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-4 md:grid-cols-2">
|
||||
{group.pages.map((page) => (
|
||||
<a
|
||||
key={page.slug}
|
||||
href={`/features/${page.slug}`}
|
||||
className="rounded-lg border border-neutral-200 bg-white p-5 transition-colors hover:border-neutral-900"
|
||||
>
|
||||
<p className="text-xs font-medium text-neutral-500">
|
||||
{page.eyebrow}
|
||||
</p>
|
||||
<h3 className="mt-2 text-lg font-semibold text-neutral-900">
|
||||
{page.title}
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-neutral-600">
|
||||
{page.description}
|
||||
</p>
|
||||
<p className="mt-3 text-sm font-medium text-neutral-900">
|
||||
Explore feature <span aria-hidden="true">→</span>
|
||||
</p>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
18
web/src/routes/_marketing/features/keyword-research.tsx
Normal file
18
web/src/routes/_marketing/features/keyword-research.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { FeaturePageTemplate } from "@/components/feature-page";
|
||||
import { featurePages } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const page = featurePages.keywordResearch;
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/keyword-research")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "Keyword Research Tool",
|
||||
description: page.description,
|
||||
path: "/features/keyword-research",
|
||||
titleSuffix: "OpenSEO",
|
||||
imageAlt: page.imageAlt,
|
||||
}),
|
||||
component: () => <FeaturePageTemplate page={page} />,
|
||||
});
|
||||
@ -4,7 +4,7 @@ import { useState } from "react";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const mcpDescription =
|
||||
"Connect OpenSEO MCP to your AI agent so it can research keywords, SERPs, domains, backlinks, and rank tracking data.";
|
||||
"Connect OpenSEO MCP so compatible AI clients can call keyword, SERP, domain, backlink, saved keyword, and rank-tracking tools.";
|
||||
|
||||
const toolCategories = [
|
||||
{
|
||||
@ -16,7 +16,7 @@ const toolCategories = [
|
||||
},
|
||||
{
|
||||
title: "Get SERP results",
|
||||
description: "See live Google results for a keyword.",
|
||||
description: "See SERP results for a keyword.",
|
||||
},
|
||||
{
|
||||
title: "Get rank tracking positions",
|
||||
@ -24,7 +24,7 @@ const toolCategories = [
|
||||
},
|
||||
{
|
||||
title: "Get saved keywords",
|
||||
description: "Pull your saved keyword lists.",
|
||||
description: "Pull saved keywords from OpenSEO.",
|
||||
},
|
||||
{
|
||||
title: "Save keywords",
|
||||
@ -70,8 +70,10 @@ function McpPage() {
|
||||
OpenSEO MCP for your AI agent
|
||||
</h1>
|
||||
<p className="mt-4 text-neutral-700 leading-relaxed">
|
||||
Connect OpenSEO to your AI agent so that it can research keywords,
|
||||
SERPs, domains, backlinks, and rank tracking data for you.
|
||||
Connect OpenSEO to your AI agent so your MCP client can call OpenSEO
|
||||
tools for keyword research, SERP results, domain overview, domain
|
||||
keywords, backlink overview, saved keywords, and rank-tracking
|
||||
positions.
|
||||
</p>
|
||||
|
||||
<a
|
||||
@ -85,10 +87,10 @@ function McpPage() {
|
||||
<section className="mt-12">
|
||||
<h2 className="text-xl font-semibold">What it does</h2>
|
||||
<p className="mt-2 text-sm leading-relaxed text-neutral-600">
|
||||
MCP lets your AI do the most tedious parts of your job faster. Connect
|
||||
OpenSEO once, then ask your agent to research keywords, inspect search
|
||||
results, compare domains, and save useful ideas back to your
|
||||
workspace.
|
||||
MCP lets compatible AI clients call OpenSEO research tools directly.
|
||||
After connecting OpenSEO through a supported MCP client, you can ask
|
||||
your agent to research keywords, inspect search results, compare
|
||||
domains, and save useful ideas back to your workspace.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@ -121,8 +123,8 @@ function McpPage() {
|
||||
<h2 className="text-xl font-semibold">Set up OpenSEO MCP</h2>
|
||||
<p className="mt-2 text-sm leading-relaxed text-neutral-600">
|
||||
The first connection sends you through OpenSEO login. After
|
||||
authorization, your AI client can call OpenSEO tools using your
|
||||
workspace permissions. The MCP server URL is{" "}
|
||||
authorization, your MCP client can call OpenSEO tools using the
|
||||
authorized OpenSEO project context and scopes. The MCP server URL is{" "}
|
||||
<code>https://app.openseo.so/mcp</code>. For the most current setup UI
|
||||
and a copyable endpoint, open{" "}
|
||||
<a
|
||||
@ -145,8 +147,9 @@ function McpPage() {
|
||||
</div>
|
||||
|
||||
<p className="mt-5 text-sm leading-relaxed text-neutral-600">
|
||||
Follow the setup steps for Claude, Claude Code, Codex, or any
|
||||
MCP-compatible AI tool.
|
||||
Follow the setup steps for Claude, Claude Code, Codex, or MCP clients
|
||||
that support remote HTTP MCP servers and the required authorization
|
||||
flow.
|
||||
</p>
|
||||
|
||||
<div className="mt-5 divide-y divide-neutral-200 overflow-hidden rounded-lg border border-neutral-200 bg-neutral-50">
|
||||
|
||||
18
web/src/routes/_marketing/features/rank-tracking.tsx
Normal file
18
web/src/routes/_marketing/features/rank-tracking.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { FeaturePageTemplate } from "@/components/feature-page";
|
||||
import { featurePages } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const page = featurePages.rankTracking;
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/rank-tracking")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "Rank Tracker",
|
||||
description: page.description,
|
||||
path: "/features/rank-tracking",
|
||||
titleSuffix: "OpenSEO",
|
||||
imageAlt: page.imageAlt,
|
||||
}),
|
||||
component: () => <FeaturePageTemplate page={page} />,
|
||||
});
|
||||
18
web/src/routes/_marketing/features/saved-keywords.tsx
Normal file
18
web/src/routes/_marketing/features/saved-keywords.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { FeaturePageTemplate } from "@/components/feature-page";
|
||||
import { featurePages } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const page = featurePages.savedKeywords;
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/saved-keywords")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "Saved Keyword Lists",
|
||||
description: page.description,
|
||||
path: "/features/saved-keywords",
|
||||
titleSuffix: "OpenSEO",
|
||||
imageAlt: page.imageAlt,
|
||||
}),
|
||||
component: () => <FeaturePageTemplate page={page} />,
|
||||
});
|
||||
18
web/src/routes/_marketing/features/site-audit.tsx
Normal file
18
web/src/routes/_marketing/features/site-audit.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { FeaturePageTemplate } from "@/components/feature-page";
|
||||
import { featurePages } from "@/lib/feature-pages";
|
||||
import { buildPageSeo } from "@/lib/seo";
|
||||
|
||||
const page = featurePages.siteAudit;
|
||||
|
||||
export const Route = createFileRoute("/_marketing/features/site-audit")({
|
||||
head: () =>
|
||||
buildPageSeo({
|
||||
title: "SEO Audit Tool",
|
||||
description: page.description,
|
||||
path: "/features/site-audit",
|
||||
titleSuffix: "OpenSEO",
|
||||
imageAlt: page.imageAlt,
|
||||
}),
|
||||
component: () => <FeaturePageTemplate page={page} />,
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user