Rebrand app to CrawlerX / MetatronCube theme
- DaisyUI themes openseo/openseo-dark -> crawlerx/crawlerx-dark on the MetatronCube palette (primary #3779b9, secondary #0f1d34, accent #4faede) - Load Inter as the app font; add --brand-gradient / .brand-gradient - Cube mark + "CrawlerX" wordmark in the sidebar and mobile top bar - Add public/crawlerx-mark.png; point transparent-logo.png at the cube mark - <title>, site.webmanifest, and all user-facing "OpenSEO" copy across src/client and src/routes -> "CrawlerX" Deferred: favicons (need resizing), agent-facing identity (MCP server name, SAM/onboarding prompts, fact sheet), web/ marketing site. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
ea8becd433
commit
b489f78129
BIN
public/Metatron_Full_logo.png
Normal file
BIN
public/Metatron_Full_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
public/Metatroncube_logo.png
Normal file
BIN
public/Metatroncube_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
BIN
public/crawlerx-mark.png
Normal file
BIN
public/crawlerx-mark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "OpenSEO",
|
"name": "CrawlerX",
|
||||||
"short_name": "OpenSEO",
|
"short_name": "CrawlerX",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-192x192.png",
|
"src": "/android-chrome-192x192.png",
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#0a0f0d",
|
"theme_color": "#3779b9",
|
||||||
"background_color": "#0a0f0d",
|
"background_color": "#ffffff",
|
||||||
"display": "standalone"
|
"display": "standalone"
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 74 KiB |
@ -11,7 +11,7 @@ type Props = {
|
|||||||
/**
|
/**
|
||||||
* Shared Markdown renderer with explicit per-element Tailwind classes.
|
* Shared Markdown renderer with explicit per-element Tailwind classes.
|
||||||
*
|
*
|
||||||
* OpenSEO doesn't ship `@tailwindcss/typography`, so `prose` classes are
|
* CrawlerX doesn't ship `@tailwindcss/typography`, so `prose` classes are
|
||||||
* no-ops — every block element is styled here instead. Tables use daisyUI's
|
* no-ops — every block element is styled here instead. Tables use daisyUI's
|
||||||
* `table table-sm` so model- and strategy-generated tables stay readable.
|
* `table table-sm` so model- and strategy-generated tables stay readable.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -129,9 +129,14 @@ export function Sidebar({ projectId, onNavigate, onClose }: SidebarProps) {
|
|||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
onClick={onNavigate}
|
onClick={onNavigate}
|
||||||
className="text-base font-semibold text-base-content"
|
className="flex items-center gap-2 text-base font-semibold text-base-content"
|
||||||
>
|
>
|
||||||
OpenSEO
|
<img
|
||||||
|
src="/crawlerx-mark.png"
|
||||||
|
alt=""
|
||||||
|
className="size-6 rounded bg-white ring-1 ring-black/5"
|
||||||
|
/>
|
||||||
|
CrawlerX
|
||||||
</Link>
|
</Link>
|
||||||
{onClose ? (
|
{onClose ? (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -79,7 +79,7 @@ const toolCategories: ToolCategory[] = [
|
|||||||
{
|
{
|
||||||
name: "save_keywords",
|
name: "save_keywords",
|
||||||
title: "Save keywords",
|
title: "Save keywords",
|
||||||
description: "Save keywords back to OpenSEO.",
|
description: "Save keywords back to CrawlerX.",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -102,7 +102,7 @@ export function AuthPageCard({
|
|||||||
<div className="text-center space-y-3">
|
<div className="text-center space-y-3">
|
||||||
<img
|
<img
|
||||||
src="/transparent-logo.png"
|
src="/transparent-logo.png"
|
||||||
alt="OpenSEO"
|
alt="CrawlerX"
|
||||||
className="mx-auto size-10 rounded-lg"
|
className="mx-auto size-10 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -58,7 +58,7 @@ export function FreePlanBanner() {
|
|||||||
return (
|
return (
|
||||||
<BannerShell variant="error">
|
<BannerShell variant="error">
|
||||||
You’ve used all your credits. {creditsActionLink} to continue
|
You’ve used all your credits. {creditsActionLink} to continue
|
||||||
using OpenSEO.
|
using CrawlerX.
|
||||||
</BannerShell>
|
</BannerShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ export function FreePlanBanner() {
|
|||||||
return (
|
return (
|
||||||
<BannerShell variant="warning">
|
<BannerShell variant="warning">
|
||||||
You’re running low on credits. {creditsActionLink} to keep using
|
You’re running low on credits. {creditsActionLink} to keep using
|
||||||
OpenSEO.
|
CrawlerX.
|
||||||
</BannerShell>
|
</BannerShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ export function FreePlanBanner() {
|
|||||||
if (isFreePlan) {
|
if (isFreePlan) {
|
||||||
return (
|
return (
|
||||||
<BannerShell variant="info">
|
<BannerShell variant="info">
|
||||||
We hope you’re enjoying OpenSEO!{" "}
|
We hope you’re enjoying CrawlerX!{" "}
|
||||||
<Link
|
<Link
|
||||||
to={SUBSCRIBE_ROUTE}
|
to={SUBSCRIBE_ROUTE}
|
||||||
search={{ upgrade: true }}
|
search={{ upgrade: true }}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ const HERO_COPY: Record<
|
|||||||
},
|
},
|
||||||
mcp: {
|
mcp: {
|
||||||
title: "Connect your AI agent",
|
title: "Connect your AI agent",
|
||||||
body: "OpenSEO is built to be used from agents like Claude. Connect once, then ask it to use OpenSEO to help build your SEO strategy.",
|
body: "CrawlerX is built to be used from agents like Claude. Connect once, then ask it to use CrawlerX to help build your SEO strategy.",
|
||||||
cta: "Show me how",
|
cta: "Show me how",
|
||||||
},
|
},
|
||||||
gsc: {
|
gsc: {
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import { dismissDashboardMcpCard } from "@/serverFunctions/dashboard";
|
|||||||
function firstPrompts(domain: string | null): string[] {
|
function firstPrompts(domain: string | null): string[] {
|
||||||
const site = domain ?? "my site";
|
const site = domain ?? "my site";
|
||||||
return [
|
return [
|
||||||
`Review ${site}. Ideas for what keywords we could target? Use OpenSEO`,
|
`Review ${site}. Ideas for what keywords we could target? Use CrawlerX`,
|
||||||
"Research my competitors top pages and keywords and tell me what's working. Use OpenSEO",
|
"Research my competitors top pages and keywords and tell me what's working. Use CrawlerX",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,14 +93,14 @@ export function McpConnectCard({
|
|||||||
</ul>
|
</ul>
|
||||||
<p className="text-xs text-base-content/50">
|
<p className="text-xs text-base-content/50">
|
||||||
Waiting for your first call — this card disappears once your agent
|
Waiting for your first call — this card disappears once your agent
|
||||||
talks to OpenSEO.
|
talks to CrawlerX.
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-2 text-sm text-base-content/70">
|
<div className="space-y-2 text-sm text-base-content/70">
|
||||||
<p>
|
<p>
|
||||||
OpenSEO is designed to give your AI agent the data it needs to
|
CrawlerX is designed to give your AI agent the data it needs to
|
||||||
build a great SEO strategy and help you execute it.
|
build a great SEO strategy and help you execute it.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export function GscReEngagementModal({
|
|||||||
New: Connect Google Search Console
|
New: Connect Google Search Console
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-base-content/70">
|
<p className="text-sm text-base-content/70">
|
||||||
Bring your real clicks, impressions, and rankings into OpenSEO and
|
Bring your real clicks, impressions, and rankings into CrawlerX and
|
||||||
query them from Claude or Codex over MCP. It never uses credits.
|
query them from Claude or Codex over MCP. It never uses credits.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,8 +14,8 @@ export function GoogleOAuthSetupWarning({
|
|||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="font-medium">Google OAuth client not configured</p>
|
<p className="font-medium">Google OAuth client not configured</p>
|
||||||
<p className="text-base-content/70">
|
<p className="text-base-content/70">
|
||||||
Add your Google client ID and secret to this OpenSEO deployment before
|
Add your Google client ID and secret to this CrawlerX deployment
|
||||||
connecting {integrationName}.
|
before connecting {integrationName}.
|
||||||
</p>
|
</p>
|
||||||
<SafeExternalLink
|
<SafeExternalLink
|
||||||
url={docsUrl}
|
url={docsUrl}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export function googleAuthErrorCopy(
|
|||||||
return {
|
return {
|
||||||
title: "Google account already connected",
|
title: "Google account already connected",
|
||||||
description:
|
description:
|
||||||
"That Google account is already linked to a different OpenSEO account. Disconnect it there first, or contact support and we'll move it over.",
|
"That Google account is already linked to a different CrawlerX account. Disconnect it there first, or contact support and we'll move it over.",
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -86,7 +86,7 @@ function SiteForm({ projectId }: { projectId: string }) {
|
|||||||
<div className="space-y-3 text-center">
|
<div className="space-y-3 text-center">
|
||||||
<img
|
<img
|
||||||
src="/transparent-logo.png"
|
src="/transparent-logo.png"
|
||||||
alt="OpenSEO"
|
alt="CrawlerX"
|
||||||
className="mx-auto size-10 rounded-lg"
|
className="mx-auto size-10 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<h1 className="text-xl font-semibold">Tell us about your website.</h1>
|
<h1 className="text-xl font-semibold">Tell us about your website.</h1>
|
||||||
|
|||||||
@ -65,8 +65,8 @@ const resolveToolLabel: ResolveToolLabel = (partType) =>
|
|||||||
TOOL_LABELS[partType] ?? null;
|
TOOL_LABELS[partType] ?? null;
|
||||||
|
|
||||||
const SUGGESTED_QUESTIONS = [
|
const SUGGESTED_QUESTIONS = [
|
||||||
"How will OpenSEO help me get more traffic?",
|
"How will CrawlerX help me get more traffic?",
|
||||||
"Compare OpenSEO and Claude",
|
"Compare CrawlerX and Claude",
|
||||||
"What do I get after I upgrade?",
|
"What do I get after I upgrade?",
|
||||||
"How does the Google Search Console integration work?",
|
"How does the Google Search Console integration work?",
|
||||||
"Right fit for consultants and agencies?",
|
"Right fit for consultants and agencies?",
|
||||||
|
|||||||
@ -65,10 +65,10 @@ export function WelcomeMessage({
|
|||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1 space-y-3 pt-0.5 text-sm">
|
<div className="min-w-0 flex-1 space-y-3 pt-0.5 text-sm">
|
||||||
<div className="space-y-3 text-base-content/80">
|
<div className="space-y-3 text-base-content/80">
|
||||||
<p>Hey, I’m Sam — welcome to OpenSEO.</p>
|
<p>Hey, I’m Sam — welcome to CrawlerX.</p>
|
||||||
<p>
|
<p>
|
||||||
To get full access to OpenSEO, you need to upgrade to the paid plan.
|
To get full access to CrawlerX, you need to upgrade to the paid
|
||||||
But, I’m here if you have any questions.
|
plan. But, I’m here if you have any questions.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
You can also{" "}
|
You can also{" "}
|
||||||
@ -146,7 +146,9 @@ export function UpgradeSidebar({
|
|||||||
<Globe className="size-4" />
|
<Globe className="size-4" />
|
||||||
</span>
|
</span>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="font-medium text-base-content/80">Previewing OpenSEO</p>
|
<p className="font-medium text-base-content/80">
|
||||||
|
Previewing CrawlerX
|
||||||
|
</p>
|
||||||
<p className="truncate" title={domain}>
|
<p className="truncate" title={domain}>
|
||||||
{domain}
|
{domain}
|
||||||
</p>
|
</p>
|
||||||
@ -187,7 +189,7 @@ export function UpgradeSidebar({
|
|||||||
{isStartingCheckout ? "Redirecting..." : "Upgrade to continue"}
|
{isStartingCheckout ? "Redirecting..." : "Upgrade to continue"}
|
||||||
</button>
|
</button>
|
||||||
<p className="text-center text-xs leading-relaxed text-base-content/55">
|
<p className="text-center text-xs leading-relaxed text-base-content/55">
|
||||||
Want advice from other OpenSEO users?{" "}
|
Want advice from other CrawlerX users?{" "}
|
||||||
<a
|
<a
|
||||||
href={DISCORD_URL}
|
href={DISCORD_URL}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -231,7 +233,7 @@ export function ChatGate({
|
|||||||
That’s all {FREE_ONBOARDING_QUESTION_LIMIT} free questions
|
That’s all {FREE_ONBOARDING_QUESTION_LIMIT} free questions
|
||||||
</p>
|
</p>
|
||||||
<p className="mx-auto mt-1 max-w-md text-xs text-base-content/70">
|
<p className="mx-auto mt-1 max-w-md text-xs text-base-content/70">
|
||||||
Upgrade to keep working with Sam and unlock the full OpenSEO app.
|
Upgrade to keep working with Sam and unlock the full CrawlerX app.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -252,7 +254,7 @@ export function ChatGate({
|
|||||||
export function ChatComposer({
|
export function ChatComposer({
|
||||||
busy,
|
busy,
|
||||||
onSend,
|
onSend,
|
||||||
placeholder = "Ask Sam about your strategy or OpenSEO…",
|
placeholder = "Ask Sam about your strategy or CrawlerX…",
|
||||||
}: {
|
}: {
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
onSend: (text: string) => void;
|
onSend: (text: string) => void;
|
||||||
|
|||||||
@ -61,7 +61,7 @@ export function PostSignupOnboarding({
|
|||||||
<div className="text-center space-y-3">
|
<div className="text-center space-y-3">
|
||||||
<img
|
<img
|
||||||
src="/transparent-logo.png"
|
src="/transparent-logo.png"
|
||||||
alt="OpenSEO"
|
alt="CrawlerX"
|
||||||
className="mx-auto size-10 rounded-lg"
|
className="mx-auto size-10 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs font-medium uppercase tracking-wide text-base-content/50">
|
<p className="text-xs font-medium uppercase tracking-wide text-base-content/50">
|
||||||
@ -70,8 +70,8 @@ export function PostSignupOnboarding({
|
|||||||
<h1 className="text-xl font-semibold">
|
<h1 className="text-xl font-semibold">
|
||||||
{title ??
|
{title ??
|
||||||
(firstName
|
(firstName
|
||||||
? `Welcome to OpenSEO, ${firstName}!`
|
? `Welcome to CrawlerX, ${firstName}!`
|
||||||
: "Welcome to OpenSEO!")}
|
: "Welcome to CrawlerX!")}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-base-content/60">
|
<p className="text-sm text-base-content/60">
|
||||||
{helperText ?? "A few quick answers to set things up."}
|
{helperText ?? "A few quick answers to set things up."}
|
||||||
@ -116,7 +116,7 @@ export function PostSignupOnboarding({
|
|||||||
/>
|
/>
|
||||||
) : step === 2 ? (
|
) : step === 2 ? (
|
||||||
<OnboardingChoiceGroup
|
<OnboardingChoiceGroup
|
||||||
title="How did you find OpenSEO?"
|
title="How did you find CrawlerX?"
|
||||||
options={[...SOURCE_OPTIONS]}
|
options={[...SOURCE_OPTIONS]}
|
||||||
selectedValues={answers.source ? [answers.source] : []}
|
selectedValues={answers.source ? [answers.source] : []}
|
||||||
onToggle={(source) => updateAnswers({ source })}
|
onToggle={(source) => updateAnswers({ source })}
|
||||||
|
|||||||
@ -46,8 +46,8 @@ export function SearchConsoleOnboardingStep() {
|
|||||||
{project ? <GscConnect projectId={project.id} /> : <Checking />}
|
{project ? <GscConnect projectId={project.id} /> : <Checking />}
|
||||||
|
|
||||||
<p className="hidden sm:block text-xs leading-relaxed text-base-content/55">
|
<p className="hidden sm:block text-xs leading-relaxed text-base-content/55">
|
||||||
For now, Search Console data flows through the OpenSEO MCP. We're
|
For now, Search Console data flows through the CrawlerX MCP. We're
|
||||||
building it into the OpenSEO app soon too.
|
building it into the CrawlerX app soon too.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -136,7 +136,7 @@ export function SamChat({
|
|||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-lg font-medium">What should we work on?</p>
|
<p className="text-lg font-medium">What should we work on?</p>
|
||||||
<p className="max-w-sm text-sm text-base-content/60">
|
<p className="max-w-sm text-sm text-base-content/60">
|
||||||
SAM is your in-app SEO agent with access to every OpenSEO research
|
SAM is your in-app SEO agent with access to every CrawlerX research
|
||||||
tool. Start a chat to get going.
|
tool. Start a chat to get going.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -20,10 +20,10 @@ export function SamSetupGate({
|
|||||||
<div className="max-w-3xl space-y-1.5">
|
<div className="max-w-3xl space-y-1.5">
|
||||||
<h2 className="text-xl font-semibold">Enable AI Features</h2>
|
<h2 className="text-xl font-semibold">Enable AI Features</h2>
|
||||||
<div className="text-sm text-base-content/68">
|
<div className="text-sm text-base-content/68">
|
||||||
SAM, OpenSEO's in-app AI agent, needs an OpenRouter API key.
|
SAM, CrawlerX's in-app AI agent, needs an OpenRouter API key.
|
||||||
Create a key on OpenRouter, set it as the{" "}
|
Create a key on OpenRouter, set it as the{" "}
|
||||||
<code>OPENROUTER_API_KEY</code> environment variable, restart
|
<code>OPENROUTER_API_KEY</code> environment variable, restart
|
||||||
OpenSEO, then confirm here.
|
CrawlerX, then confirm here.
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-base-content/50">
|
<div className="text-xs text-base-content/50">
|
||||||
Step-by-step instructions for every deployment are in the{" "}
|
Step-by-step instructions for every deployment are in the{" "}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ function BetaNotice() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-1.5 text-xs text-base-content/70">
|
<p className="mt-1.5 text-xs text-base-content/70">
|
||||||
For more powerful AI workflows, use the OpenSEO MCP with your own agent
|
For more powerful AI workflows, use the CrawlerX MCP with your own agent
|
||||||
like Claude Code or Hermes.
|
like Claude Code or Hermes.
|
||||||
</p>
|
</p>
|
||||||
<Link to="/ai" className="link link-primary mt-1.5 inline-block text-xs">
|
<Link to="/ai" className="link link-primary mt-1.5 inline-block text-xs">
|
||||||
|
|||||||
@ -180,8 +180,16 @@ function MobileTopBar({
|
|||||||
>
|
>
|
||||||
<Menu className="h-5 w-5" />
|
<Menu className="h-5 w-5" />
|
||||||
</button>
|
</button>
|
||||||
<Link to="/" className="ml-1 font-semibold text-base-content">
|
<Link
|
||||||
OpenSEO
|
to="/"
|
||||||
|
className="ml-1 flex items-center gap-2 font-semibold text-base-content"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/crawlerx-mark.png"
|
||||||
|
alt=""
|
||||||
|
className="size-6 rounded bg-white ring-1 ring-black/5"
|
||||||
|
/>
|
||||||
|
CrawlerX
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -19,7 +19,7 @@ function SeoApiStatusBanners({
|
|||||||
<div className="alert alert-warning">
|
<div className="alert alert-warning">
|
||||||
<AlertTriangle className="size-4 shrink-0" />
|
<AlertTriangle className="size-4 shrink-0" />
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
Setup needed: add your DataForSEO API key to use OpenSEO
|
Setup needed: add your DataForSEO API key to use CrawlerX
|
||||||
features. See the quick steps on the{" "}
|
features. See the quick steps on the{" "}
|
||||||
<Link
|
<Link
|
||||||
{...dataforseoHelpLinkOptions}
|
{...dataforseoHelpLinkOptions}
|
||||||
@ -119,7 +119,7 @@ const MissingSeoSetupModal = React.forwardRef<
|
|||||||
id="dataforseo-setup-description"
|
id="dataforseo-setup-description"
|
||||||
className="text-sm text-base-content/75"
|
className="text-sm text-base-content/75"
|
||||||
>
|
>
|
||||||
Add your DataForSEO API key to start using OpenSEO.
|
Add your DataForSEO API key to start using CrawlerX.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
describe("getStandardErrorMessage", () => {
|
describe("getStandardErrorMessage", () => {
|
||||||
it("maps known error codes to standard copy", () => {
|
it("maps known error codes to standard copy", () => {
|
||||||
expect(getStandardErrorMessage(new Error("PAYMENT_REQUIRED"))).toBe(
|
expect(getStandardErrorMessage(new Error("PAYMENT_REQUIRED"))).toBe(
|
||||||
"An active hosted subscription is required before you can use OpenSEO.",
|
"An active hosted subscription is required before you can use CrawlerX.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import { isErrorCode, type ErrorCode } from "@/shared/error-codes";
|
|||||||
const STANDARD_MESSAGES: Record<ErrorCode, string> = {
|
const STANDARD_MESSAGES: Record<ErrorCode, string> = {
|
||||||
UNAUTHENTICATED: "Please sign in and try again.",
|
UNAUTHENTICATED: "Please sign in and try again.",
|
||||||
AUTH_CONFIG_MISSING:
|
AUTH_CONFIG_MISSING:
|
||||||
"OpenSEO auth is not configured. Follow the README setup steps for Cloudflare Access.",
|
"CrawlerX auth is not configured. Follow the README setup steps for Cloudflare Access.",
|
||||||
PAYMENT_REQUIRED:
|
PAYMENT_REQUIRED:
|
||||||
"An active hosted subscription is required before you can use OpenSEO.",
|
"An active hosted subscription is required before you can use CrawlerX.",
|
||||||
INSUFFICIENT_CREDITS:
|
INSUFFICIENT_CREDITS:
|
||||||
"You've run out of credits. Add more credits or upgrade your plan to continue.",
|
"You've run out of credits. Add more credits or upgrade your plan to continue.",
|
||||||
FORBIDDEN: "You do not have access to this resource.",
|
FORBIDDEN: "You do not have access to this resource.",
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import * as React from "react";
|
|||||||
|
|
||||||
export type ThemePreference = "system" | "light" | "dark";
|
export type ThemePreference = "system" | "light" | "dark";
|
||||||
|
|
||||||
const LIGHT_THEME_NAME = "openseo";
|
const LIGHT_THEME_NAME = "crawlerx";
|
||||||
const DARK_THEME_NAME = "openseo-dark";
|
const DARK_THEME_NAME = "crawlerx-dark";
|
||||||
|
|
||||||
const THEME_STORAGE_KEY = "theme-preference";
|
const THEME_STORAGE_KEY = "theme-preference";
|
||||||
const THEME_CHANGE_EVENT = "theme-preference-change";
|
const THEME_CHANGE_EVENT = "theme-preference-change";
|
||||||
|
|||||||
@ -1,7 +1,26 @@
|
|||||||
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--container-8xl: 88rem;
|
--container-8xl: 88rem;
|
||||||
|
--font-sans:
|
||||||
|
"Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
|
||||||
|
"Helvetica Neue", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CrawlerX brand gradient (MetatronCube blue → deep black), used on the
|
||||||
|
sign-in surface and the sidebar header. */
|
||||||
|
:root {
|
||||||
|
--brand-gradient: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
#3779b9 0%,
|
||||||
|
#0f1d34 70%,
|
||||||
|
#00060c 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-gradient {
|
||||||
|
background-image: var(--brand-gradient);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@ -19,26 +38,26 @@
|
|||||||
exclude: properties;
|
exclude: properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Light theme */
|
/* Light theme — CrawlerX / MetatronCube brand (primary #3779b9) */
|
||||||
@plugin "daisyui/theme" {
|
@plugin "daisyui/theme" {
|
||||||
name: "openseo";
|
name: "crawlerx";
|
||||||
default: true;
|
default: true;
|
||||||
prefersdark: false;
|
prefersdark: false;
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
--color-base-100: oklch(100% 0 0);
|
--color-base-100: #ffffff;
|
||||||
--color-base-200: oklch(97% 0 0);
|
--color-base-200: #f6f8fa;
|
||||||
--color-base-300: oklch(92% 0 0);
|
--color-base-300: #e6eaef;
|
||||||
--color-base-content: oklch(20% 0 0);
|
--color-base-content: #151515;
|
||||||
--color-primary: oklch(50% 0.12 262);
|
--color-primary: #3779b9;
|
||||||
--color-primary-content: oklch(100% 0 0);
|
--color-primary-content: #ffffff;
|
||||||
--color-secondary: oklch(55% 0.15 145);
|
--color-secondary: #0f1d34;
|
||||||
--color-secondary-content: oklch(100% 0 0);
|
--color-secondary-content: #ffffff;
|
||||||
--color-accent: oklch(50% 0.12 262);
|
--color-accent: #4faede;
|
||||||
--color-accent-content: oklch(100% 0 0);
|
--color-accent-content: #06263a;
|
||||||
--color-neutral: oklch(25% 0 0);
|
--color-neutral: #151515;
|
||||||
--color-neutral-content: oklch(100% 0 0);
|
--color-neutral-content: #ffffff;
|
||||||
--color-info: oklch(70% 0.15 230);
|
--color-info: #3779b9;
|
||||||
--color-info-content: oklch(100% 0 0);
|
--color-info-content: #ffffff;
|
||||||
--color-success: oklch(65% 0.18 145);
|
--color-success: oklch(65% 0.18 145);
|
||||||
--color-success-content: oklch(100% 0 0);
|
--color-success-content: oklch(100% 0 0);
|
||||||
--color-warning: oklch(80% 0.15 80);
|
--color-warning: oklch(80% 0.15 80);
|
||||||
@ -57,23 +76,23 @@
|
|||||||
|
|
||||||
/* Dark theme - auto-activates via prefers-color-scheme */
|
/* Dark theme - auto-activates via prefers-color-scheme */
|
||||||
@plugin "daisyui/theme" {
|
@plugin "daisyui/theme" {
|
||||||
name: "openseo-dark";
|
name: "crawlerx-dark";
|
||||||
prefersdark: true;
|
prefersdark: true;
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
--color-base-100: oklch(18% 0 0);
|
--color-base-100: #141821;
|
||||||
--color-base-200: oklch(12% 0 0);
|
--color-base-200: #0e121b;
|
||||||
--color-base-300: oklch(27% 0 0);
|
--color-base-300: #232a37;
|
||||||
--color-base-content: oklch(92% 0 0);
|
--color-base-content: #e8ebf0;
|
||||||
--color-primary: oklch(66% 0.12 262);
|
--color-primary: #3f8ad4;
|
||||||
--color-primary-content: oklch(100% 0 0);
|
--color-primary-content: #ffffff;
|
||||||
--color-secondary: oklch(60% 0.15 145);
|
--color-secondary: #9fb3c8;
|
||||||
--color-secondary-content: oklch(100% 0 0);
|
--color-secondary-content: #0e121b;
|
||||||
--color-accent: oklch(66% 0.12 262);
|
--color-accent: #4faede;
|
||||||
--color-accent-content: oklch(100% 0 0);
|
--color-accent-content: #06263a;
|
||||||
--color-neutral: oklch(85% 0 0);
|
--color-neutral: #e8ebf0;
|
||||||
--color-neutral-content: oklch(20% 0 0);
|
--color-neutral-content: #141821;
|
||||||
--color-info: oklch(70% 0.15 230);
|
--color-info: #3f8ad4;
|
||||||
--color-info-content: oklch(100% 0 0);
|
--color-info-content: #ffffff;
|
||||||
--color-success: oklch(65% 0.18 145);
|
--color-success: oklch(65% 0.18 145);
|
||||||
--color-success-content: oklch(100% 0 0);
|
--color-success-content: oklch(100% 0 0);
|
||||||
--color-warning: oklch(80% 0.15 80);
|
--color-warning: oklch(80% 0.15 80);
|
||||||
@ -153,7 +172,7 @@ select {
|
|||||||
base-200, which is darker than the base-100 content panel in dark mode,
|
base-200, which is darker than the base-100 content panel in dark mode,
|
||||||
so they read as sunken/disabled. Colored variants (primary, error, ...)
|
so they read as sunken/disabled. Colored variants (primary, error, ...)
|
||||||
set their own --btn-color and are excluded. */
|
set their own --btn-color and are excluded. */
|
||||||
html[data-theme="openseo-dark"]
|
html[data-theme="crawlerx-dark"]
|
||||||
.btn:not(
|
.btn:not(
|
||||||
.btn-primary,
|
.btn-primary,
|
||||||
.btn-error,
|
.btn-error,
|
||||||
@ -168,7 +187,7 @@ html[data-theme="openseo-dark"]
|
|||||||
/* Light mode: the base-200 default button is nearly invisible on the
|
/* Light mode: the base-200 default button is nearly invisible on the
|
||||||
base-100 content panel; use a white surface with a visible border,
|
base-100 content panel; use a white surface with a visible border,
|
||||||
matching inputs/selects. */
|
matching inputs/selects. */
|
||||||
html[data-theme="openseo"]
|
html[data-theme="crawlerx"]
|
||||||
.btn:not(
|
.btn:not(
|
||||||
.btn-primary,
|
.btn-primary,
|
||||||
.btn-error,
|
.btn-error,
|
||||||
@ -251,28 +270,28 @@ html[data-theme="openseo"]
|
|||||||
--tw-ring-color: color-mix(in oklab, #d946ef 30%, transparent);
|
--tw-ring-color: color-mix(in oklab, #d946ef 30%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="openseo-dark"] .tag-chip-slate {
|
html[data-theme="crawlerx-dark"] .tag-chip-slate {
|
||||||
color: #cbd5e1;
|
color: #cbd5e1;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .tag-chip-rose {
|
html[data-theme="crawlerx-dark"] .tag-chip-rose {
|
||||||
color: #fda4af;
|
color: #fda4af;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .tag-chip-amber {
|
html[data-theme="crawlerx-dark"] .tag-chip-amber {
|
||||||
color: #fcd34d;
|
color: #fcd34d;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .tag-chip-lime {
|
html[data-theme="crawlerx-dark"] .tag-chip-lime {
|
||||||
color: #d9f99d;
|
color: #d9f99d;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .tag-chip-emerald {
|
html[data-theme="crawlerx-dark"] .tag-chip-emerald {
|
||||||
color: #6ee7b7;
|
color: #6ee7b7;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .tag-chip-sky {
|
html[data-theme="crawlerx-dark"] .tag-chip-sky {
|
||||||
color: #7dd3fc;
|
color: #7dd3fc;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .tag-chip-violet {
|
html[data-theme="crawlerx-dark"] .tag-chip-violet {
|
||||||
color: #c4b5fd;
|
color: #c4b5fd;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .tag-chip-fuchsia {
|
html[data-theme="crawlerx-dark"] .tag-chip-fuchsia {
|
||||||
color: #f5d0fe;
|
color: #f5d0fe;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,29 +342,29 @@ html[data-theme="openseo-dark"] .tag-chip-fuchsia {
|
|||||||
--tw-ring-color: color-mix(in oklab, #b91c1c 32%, transparent);
|
--tw-ring-color: color-mix(in oklab, #b91c1c 32%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="openseo-dark"] .score-tier-na {
|
html[data-theme="crawlerx-dark"] .score-tier-na {
|
||||||
color: #cbd5e1;
|
color: #cbd5e1;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .score-tier-1 {
|
html[data-theme="crawlerx-dark"] .score-tier-1 {
|
||||||
color: #6ee7b7;
|
color: #6ee7b7;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .score-tier-2 {
|
html[data-theme="crawlerx-dark"] .score-tier-2 {
|
||||||
color: #bef264;
|
color: #bef264;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .score-tier-3 {
|
html[data-theme="crawlerx-dark"] .score-tier-3 {
|
||||||
color: #fde047;
|
color: #fde047;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .score-tier-4 {
|
html[data-theme="crawlerx-dark"] .score-tier-4 {
|
||||||
color: #fdba74;
|
color: #fdba74;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .score-tier-5 {
|
html[data-theme="crawlerx-dark"] .score-tier-5 {
|
||||||
color: #fca5a5;
|
color: #fca5a5;
|
||||||
}
|
}
|
||||||
html[data-theme="openseo-dark"] .score-tier-6 {
|
html[data-theme="crawlerx-dark"] .score-tier-6 {
|
||||||
color: #fda4af;
|
color: #fda4af;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="openseo-dark"] {
|
html[data-theme="crawlerx-dark"] {
|
||||||
--trend-grid-color: oklch(78% 0.01 255 / 0.22);
|
--trend-grid-color: oklch(78% 0.01 255 / 0.22);
|
||||||
--trend-axis-color: oklch(84% 0.01 255 / 0.62);
|
--trend-axis-color: oklch(84% 0.01 255 / 0.62);
|
||||||
--trend-fill-start-opacity: 0.24;
|
--trend-fill-start-opacity: 0.24;
|
||||||
@ -355,18 +374,18 @@ html[data-theme="openseo-dark"] {
|
|||||||
--trend-tooltip-shadow: oklch(0% 0 0 / 0.35);
|
--trend-tooltip-shadow: oklch(0% 0 0 / 0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="openseo-dark"] .alert-warning {
|
html[data-theme="crawlerx-dark"] .alert-warning {
|
||||||
@apply border-warning/60 bg-warning/20;
|
@apply border-warning/60 bg-warning/20;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="openseo-dark"] .alert-info {
|
html[data-theme="crawlerx-dark"] .alert-info {
|
||||||
@apply border-info/60 bg-info/20;
|
@apply border-info/60 bg-info/20;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="openseo-dark"] .alert-success {
|
html[data-theme="crawlerx-dark"] .alert-success {
|
||||||
@apply border-success/60 bg-success/20;
|
@apply border-success/60 bg-success/20;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="openseo-dark"] .alert-error {
|
html[data-theme="crawlerx-dark"] .alert-error {
|
||||||
@apply border-error/60 bg-error/20;
|
@apply border-error/60 bg-error/20;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export const Route = createRootRoute({
|
|||||||
head: () => ({
|
head: () => ({
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{
|
||||||
title: "OpenSEO",
|
title: "CrawlerX",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
charSet: "utf-8",
|
charSet: "utf-8",
|
||||||
|
|||||||
@ -53,8 +53,9 @@ function AiPage() {
|
|||||||
<div className="mx-auto max-w-3xl">
|
<div className="mx-auto max-w-3xl">
|
||||||
<h1 className="text-2xl font-semibold">AI & MCP</h1>
|
<h1 className="text-2xl font-semibold">AI & MCP</h1>
|
||||||
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
||||||
Connect your AI agent to OpenSEO. Run keyword research, SERP analysis,
|
Connect your AI agent to CrawlerX. Run keyword research, SERP
|
||||||
domain lookups, and backlink reviews from your editor or chat.
|
analysis, domain lookups, and backlink reviews from your editor or
|
||||||
|
chat.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{getAuthMode(import.meta.env.AUTH_MODE) === "cloudflare_access" ? (
|
{getAuthMode(import.meta.env.AUTH_MODE) === "cloudflare_access" ? (
|
||||||
@ -92,9 +93,9 @@ function AiPage() {
|
|||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-2.5 text-xs text-base-content/55 leading-relaxed">
|
<p className="mt-2.5 text-xs text-base-content/55 leading-relaxed">
|
||||||
Paste this into any MCP client. This URL points at the OpenSEO
|
Paste this into any MCP client. This URL points at the CrawlerX
|
||||||
instance you are using now, whether hosted, self-hosted, or local.
|
instance you are using now, whether hosted, self-hosted, or local.
|
||||||
Sign in with OpenSEO when prompted.
|
Sign in with CrawlerX when prompted.
|
||||||
</p>
|
</p>
|
||||||
{isHostedClientAuthMode() ? (
|
{isHostedClientAuthMode() ? (
|
||||||
<p className="mt-2 text-xs text-base-content/55">
|
<p className="mt-2 text-xs text-base-content/55">
|
||||||
@ -154,9 +155,9 @@ function AiPage() {
|
|||||||
.
|
.
|
||||||
</li>
|
</li>
|
||||||
<li>Paste the MCP URL above and click Add.</li>
|
<li>Paste the MCP URL above and click Add.</li>
|
||||||
<li>Approve the OpenSEO login when prompted.</li>
|
<li>Approve the CrawlerX login when prompted.</li>
|
||||||
<li>
|
<li>
|
||||||
Optional: after OpenSEO connects, click{" "}
|
Optional: after CrawlerX connects, click{" "}
|
||||||
<span className="font-medium text-base-content">
|
<span className="font-medium text-base-content">
|
||||||
Configure
|
Configure
|
||||||
</span>
|
</span>
|
||||||
@ -216,17 +217,17 @@ function AiPage() {
|
|||||||
.
|
.
|
||||||
</li>
|
</li>
|
||||||
<li>Paste the MCP URL above.</li>
|
<li>Paste the MCP URL above.</li>
|
||||||
<li>Approve the OpenSEO login when prompted.</li>
|
<li>Approve the CrawlerX login when prompted.</li>
|
||||||
</ol>
|
</ol>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="mt-12">
|
<section className="mt-12">
|
||||||
<h2 className="text-base font-semibold">OpenSEO Skills</h2>
|
<h2 className="text-base font-semibold">CrawlerX Skills</h2>
|
||||||
<p className="mt-1.5 text-sm text-base-content/70 leading-relaxed">
|
<p className="mt-1.5 text-sm text-base-content/70 leading-relaxed">
|
||||||
Skills give Codex and Claude Code reusable SEO workflows that can
|
Skills give Codex and Claude Code reusable SEO workflows that can
|
||||||
call your OpenSEO MCP tools when live SERP, keyword, backlink, or
|
call your CrawlerX MCP tools when live SERP, keyword, backlink, or
|
||||||
domain data is needed.
|
domain data is needed.
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-4 divide-y divide-base-300 overflow-hidden rounded-lg border border-base-300 bg-base-200">
|
<div className="mt-4 divide-y divide-base-300 overflow-hidden rounded-lg border border-base-300 bg-base-200">
|
||||||
@ -237,7 +238,7 @@ function AiPage() {
|
|||||||
>
|
>
|
||||||
<CodeBlock code={SKILLS_INSTALL} />
|
<CodeBlock code={SKILLS_INSTALL} />
|
||||||
<p className="text-sm text-base-content/70">
|
<p className="text-sm text-base-content/70">
|
||||||
You can also auto-accept each OpenSEO skill:
|
You can also auto-accept each CrawlerX skill:
|
||||||
</p>
|
</p>
|
||||||
<CodeBlock code={ALL_SKILLS_INSTALL} />
|
<CodeBlock code={ALL_SKILLS_INSTALL} />
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
@ -320,7 +321,7 @@ function AiPage() {
|
|||||||
{
|
{
|
||||||
title: "In-app SEO Research Agent",
|
title: "In-app SEO Research Agent",
|
||||||
description:
|
description:
|
||||||
"Ask questions and run research without leaving OpenSEO",
|
"Ask questions and run research without leaving CrawlerX",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Content Assistant",
|
title: "Content Assistant",
|
||||||
|
|||||||
@ -196,7 +196,7 @@ function BillingPage() {
|
|||||||
</div>
|
</div>
|
||||||
<ul className="space-y-1.5">
|
<ul className="space-y-1.5">
|
||||||
{[
|
{[
|
||||||
"Access to all OpenSEO features",
|
"Access to all CrawlerX features",
|
||||||
"Includes $10.00 of Usage Credits each month",
|
"Includes $10.00 of Usage Credits each month",
|
||||||
].map((item) => (
|
].map((item) => (
|
||||||
<li
|
<li
|
||||||
|
|||||||
@ -16,7 +16,7 @@ function DataforseoApiKeyHelpPage() {
|
|||||||
Set up your DataForSEO API key
|
Set up your DataForSEO API key
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-base-content/70">
|
<p className="text-sm text-base-content/70">
|
||||||
OpenSEO needs the <code>DATAFORSEO_API_KEY</code> secret before
|
CrawlerX needs the <code>DATAFORSEO_API_KEY</code> secret before
|
||||||
keyword, domain, and SEO data workflows can run.
|
keyword, domain, and SEO data workflows can run.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -62,7 +62,7 @@ function DataforseoApiKeyHelpPage() {
|
|||||||
<li>
|
<li>
|
||||||
In Cloudflare, go to <code>Compute</code> ->{" "}
|
In Cloudflare, go to <code>Compute</code> ->{" "}
|
||||||
<code>Workers & Pages</code>
|
<code>Workers & Pages</code>
|
||||||
and open your OpenSEO Worker.
|
and open your CrawlerX Worker.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Open <code>Settings</code>.
|
Open <code>Settings</code>.
|
||||||
|
|||||||
@ -16,9 +16,9 @@ function OpenrouterApiKeyHelpPage() {
|
|||||||
Set up your OpenRouter API key
|
Set up your OpenRouter API key
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-base-content/70">
|
<p className="text-sm text-base-content/70">
|
||||||
OpenSEO needs the <code>OPENROUTER_API_KEY</code> secret before AI
|
CrawlerX needs the <code>OPENROUTER_API_KEY</code> secret before
|
||||||
features like SAM, the in-app SEO agent, can run. It is optional —
|
AI features like SAM, the in-app SEO agent, can run. It is
|
||||||
everything else in OpenSEO works without it.
|
optional — everything else in CrawlerX works without it.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -64,7 +64,7 @@ function OpenrouterApiKeyHelpPage() {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>Restart OpenSEO.</li>
|
<li>Restart CrawlerX.</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -78,7 +78,7 @@ function OpenrouterApiKeyHelpPage() {
|
|||||||
<li>
|
<li>
|
||||||
In Cloudflare, go to <code>Compute</code> ->{" "}
|
In Cloudflare, go to <code>Compute</code> ->{" "}
|
||||||
<code>Workers & Pages</code>
|
<code>Workers & Pages</code>
|
||||||
and open your OpenSEO Worker.
|
and open your CrawlerX Worker.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Open <code>Settings</code>.
|
Open <code>Settings</code>.
|
||||||
|
|||||||
@ -76,7 +76,7 @@ function IndexRedirect() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-full p-4">
|
<div className="flex items-center justify-center h-full p-4">
|
||||||
<UnauthenticatedErrorCard
|
<UnauthenticatedErrorCard
|
||||||
message="Please sign in to access your OpenSEO organization."
|
message="Please sign in to access your CrawlerX organization."
|
||||||
onRetry={() => {
|
onRetry={() => {
|
||||||
void refetch();
|
void refetch();
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -95,7 +95,7 @@ function PersonalSettings() {
|
|||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-start justify-between gap-6">
|
<div className="flex items-start justify-between gap-6">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm">Help improve OpenSEO</p>
|
<p className="text-sm">Help improve CrawlerX</p>
|
||||||
<p className="mt-1 text-sm text-base-content/60">
|
<p className="mt-1 text-sm text-base-content/60">
|
||||||
Share analytics and usage data.
|
Share analytics and usage data.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -32,7 +32,7 @@ function SupportPage() {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="mt-2 text-sm text-base-content/60">
|
<p className="mt-2 text-sm text-base-content/60">
|
||||||
We want to talk to you! We're super open to feedback and want to learn
|
We want to talk to you! We're super open to feedback and want to learn
|
||||||
how you work so we can make OpenSEO better.
|
how you work so we can make CrawlerX better.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mt-8 space-y-3">
|
<div className="mt-8 space-y-3">
|
||||||
|
|||||||
@ -87,7 +87,7 @@ function SignUpPage() {
|
|||||||
redirect_to: redirectTo,
|
redirect_to: redirectTo,
|
||||||
});
|
});
|
||||||
const resolvedName =
|
const resolvedName =
|
||||||
value.name.trim() || email.split("@")[0] || "OpenSEO User";
|
value.name.trim() || email.split("@")[0] || "CrawlerX User";
|
||||||
const verificationCallbackURL = new URL(
|
const verificationCallbackURL = new URL(
|
||||||
"/verify-email",
|
"/verify-email",
|
||||||
window.location.origin,
|
window.location.origin,
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export const Route = createFileRoute("/_authenticated/oauth-consent")({
|
|||||||
const SCOPES = [
|
const SCOPES = [
|
||||||
{
|
{
|
||||||
icon: Database,
|
icon: Database,
|
||||||
label: "Read your OpenSEO data",
|
label: "Read your CrawlerX data",
|
||||||
description: "Projects, keyword reports, and audit results.",
|
description: "Projects, keyword reports, and audit results.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -74,12 +74,12 @@ function OAuthConsentPage() {
|
|||||||
<div className="flex flex-col items-center text-center">
|
<div className="flex flex-col items-center text-center">
|
||||||
<img
|
<img
|
||||||
src="/transparent-logo.png"
|
src="/transparent-logo.png"
|
||||||
alt="OpenSEO"
|
alt="CrawlerX"
|
||||||
className="size-10 rounded-lg"
|
className="size-10 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<h1 className="mt-5 text-xl font-semibold">Authorize MCP access</h1>
|
<h1 className="mt-5 text-xl font-semibold">Authorize MCP access</h1>
|
||||||
<p className="mt-2 text-sm text-base-content/70">
|
<p className="mt-2 text-sm text-base-content/70">
|
||||||
An MCP client is requesting access to your OpenSEO workspace.
|
An MCP client is requesting access to your CrawlerX workspace.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -136,7 +136,7 @@ function SubscribePage() {
|
|||||||
<div className="w-full max-w-xs space-y-4 text-center">
|
<div className="w-full max-w-xs space-y-4 text-center">
|
||||||
<img
|
<img
|
||||||
src="/transparent-logo.png"
|
src="/transparent-logo.png"
|
||||||
alt="OpenSEO"
|
alt="CrawlerX"
|
||||||
className="mx-auto size-10 rounded-lg"
|
className="mx-auto size-10 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<h1 className="text-xl font-semibold">
|
<h1 className="text-xl font-semibold">
|
||||||
@ -163,7 +163,7 @@ function SubscribePage() {
|
|||||||
<div className="text-center space-y-3">
|
<div className="text-center space-y-3">
|
||||||
<img
|
<img
|
||||||
src="/transparent-logo.png"
|
src="/transparent-logo.png"
|
||||||
alt="OpenSEO"
|
alt="CrawlerX"
|
||||||
className="mx-auto size-10 rounded-lg"
|
className="mx-auto size-10 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<h1 className="text-xl font-semibold">Billing unavailable</h1>
|
<h1 className="text-xl font-semibold">Billing unavailable</h1>
|
||||||
@ -223,15 +223,15 @@ function SubscribePage() {
|
|||||||
<div className="text-center space-y-3">
|
<div className="text-center space-y-3">
|
||||||
<img
|
<img
|
||||||
src="/transparent-logo.png"
|
src="/transparent-logo.png"
|
||||||
alt="OpenSEO"
|
alt="CrawlerX"
|
||||||
className="mx-auto size-10 rounded-lg"
|
className="mx-auto size-10 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<h1 className="text-xl font-semibold">
|
<h1 className="text-xl font-semibold">
|
||||||
{isUpgradeFlow
|
{isUpgradeFlow
|
||||||
? "Upgrade your plan"
|
? "Upgrade your plan"
|
||||||
: firstName
|
: firstName
|
||||||
? `Welcome to OpenSEO, ${firstName}!`
|
? `Welcome to CrawlerX, ${firstName}!`
|
||||||
: "Welcome to OpenSEO!"}
|
: "Welcome to CrawlerX!"}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-base-content/60">
|
<p className="text-sm text-base-content/60">
|
||||||
SEO on your terms. All your SEO tools in one place at a fair price.
|
SEO on your terms. All your SEO tools in one place at a fair price.
|
||||||
|
|||||||
@ -39,7 +39,7 @@ function SignedOutInvitationCard({ invitationId }: { invitationId: string }) {
|
|||||||
return (
|
return (
|
||||||
<AuthPageCard title="You’re invited">
|
<AuthPageCard title="You’re invited">
|
||||||
<p className="text-sm text-base-content/70">
|
<p className="text-sm text-base-content/70">
|
||||||
You’ve been invited to join an organization on OpenSEO. Sign in
|
You’ve been invited to join an organization on CrawlerX. Sign in
|
||||||
with the email address that received the invitation to accept it.
|
with the email address that received the invitation to accept it.
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@ -212,7 +212,7 @@ function InvitationCard({
|
|||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{invitationQuery.data.organizationName}
|
{invitationQuery.data.organizationName}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
on OpenSEO.
|
on CrawlerX.
|
||||||
</p>
|
</p>
|
||||||
{actionError ? <p className="text-sm text-error">{actionError}</p> : null}
|
{actionError ? <p className="text-sm text-error">{actionError}</p> : null}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@ -39,7 +39,7 @@ function AuthErrorPage() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Link to="/" className="btn btn-soft w-full">
|
<Link to="/" className="btn btn-soft w-full">
|
||||||
Back to OpenSEO
|
Back to CrawlerX
|
||||||
</Link>
|
</Link>
|
||||||
</AuthPageCard>
|
</AuthPageCard>
|
||||||
</AuthPageShell>
|
</AuthPageShell>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user