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",
|
||||
"short_name": "OpenSEO",
|
||||
"name": "CrawlerX",
|
||||
"short_name": "CrawlerX",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
@ -13,7 +13,7 @@
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#0a0f0d",
|
||||
"background_color": "#0a0f0d",
|
||||
"theme_color": "#3779b9",
|
||||
"background_color": "#ffffff",
|
||||
"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.
|
||||
*
|
||||
* 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
|
||||
* `table table-sm` so model- and strategy-generated tables stay readable.
|
||||
*
|
||||
|
||||
@ -129,9 +129,14 @@ export function Sidebar({ projectId, onNavigate, onClose }: SidebarProps) {
|
||||
<Link
|
||||
to="/"
|
||||
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>
|
||||
{onClose ? (
|
||||
<button
|
||||
|
||||
@ -79,7 +79,7 @@ const toolCategories: ToolCategory[] = [
|
||||
{
|
||||
name: "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">
|
||||
<img
|
||||
src="/transparent-logo.png"
|
||||
alt="OpenSEO"
|
||||
alt="CrawlerX"
|
||||
className="mx-auto size-10 rounded-lg"
|
||||
/>
|
||||
<div>
|
||||
|
||||
@ -58,7 +58,7 @@ export function FreePlanBanner() {
|
||||
return (
|
||||
<BannerShell variant="error">
|
||||
You’ve used all your credits. {creditsActionLink} to continue
|
||||
using OpenSEO.
|
||||
using CrawlerX.
|
||||
</BannerShell>
|
||||
);
|
||||
}
|
||||
@ -67,7 +67,7 @@ export function FreePlanBanner() {
|
||||
return (
|
||||
<BannerShell variant="warning">
|
||||
You’re running low on credits. {creditsActionLink} to keep using
|
||||
OpenSEO.
|
||||
CrawlerX.
|
||||
</BannerShell>
|
||||
);
|
||||
}
|
||||
@ -75,7 +75,7 @@ export function FreePlanBanner() {
|
||||
if (isFreePlan) {
|
||||
return (
|
||||
<BannerShell variant="info">
|
||||
We hope you’re enjoying OpenSEO!{" "}
|
||||
We hope you’re enjoying CrawlerX!{" "}
|
||||
<Link
|
||||
to={SUBSCRIBE_ROUTE}
|
||||
search={{ upgrade: true }}
|
||||
|
||||
@ -40,7 +40,7 @@ const HERO_COPY: Record<
|
||||
},
|
||||
mcp: {
|
||||
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",
|
||||
},
|
||||
gsc: {
|
||||
|
||||
@ -8,8 +8,8 @@ import { dismissDashboardMcpCard } from "@/serverFunctions/dashboard";
|
||||
function firstPrompts(domain: string | null): string[] {
|
||||
const site = domain ?? "my site";
|
||||
return [
|
||||
`Review ${site}. Ideas for what keywords we could target? Use OpenSEO`,
|
||||
"Research my competitors top pages and keywords and tell me what's working. 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 CrawlerX",
|
||||
];
|
||||
}
|
||||
|
||||
@ -93,14 +93,14 @@ export function McpConnectCard({
|
||||
</ul>
|
||||
<p className="text-xs text-base-content/50">
|
||||
Waiting for your first call — this card disappears once your agent
|
||||
talks to OpenSEO.
|
||||
talks to CrawlerX.
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="space-y-2 text-sm text-base-content/70">
|
||||
<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.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@ -106,7 +106,7 @@ export function GscReEngagementModal({
|
||||
New: Connect Google Search Console
|
||||
</h2>
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -14,8 +14,8 @@ export function GoogleOAuthSetupWarning({
|
||||
<div className="space-y-1">
|
||||
<p className="font-medium">Google OAuth client not configured</p>
|
||||
<p className="text-base-content/70">
|
||||
Add your Google client ID and secret to this OpenSEO deployment before
|
||||
connecting {integrationName}.
|
||||
Add your Google client ID and secret to this CrawlerX deployment
|
||||
before connecting {integrationName}.
|
||||
</p>
|
||||
<SafeExternalLink
|
||||
url={docsUrl}
|
||||
|
||||
@ -31,7 +31,7 @@ export function googleAuthErrorCopy(
|
||||
return {
|
||||
title: "Google account already connected",
|
||||
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:
|
||||
return {
|
||||
|
||||
@ -86,7 +86,7 @@ function SiteForm({ projectId }: { projectId: string }) {
|
||||
<div className="space-y-3 text-center">
|
||||
<img
|
||||
src="/transparent-logo.png"
|
||||
alt="OpenSEO"
|
||||
alt="CrawlerX"
|
||||
className="mx-auto size-10 rounded-lg"
|
||||
/>
|
||||
<h1 className="text-xl font-semibold">Tell us about your website.</h1>
|
||||
|
||||
@ -65,8 +65,8 @@ const resolveToolLabel: ResolveToolLabel = (partType) =>
|
||||
TOOL_LABELS[partType] ?? null;
|
||||
|
||||
const SUGGESTED_QUESTIONS = [
|
||||
"How will OpenSEO help me get more traffic?",
|
||||
"Compare OpenSEO and Claude",
|
||||
"How will CrawlerX help me get more traffic?",
|
||||
"Compare CrawlerX and Claude",
|
||||
"What do I get after I upgrade?",
|
||||
"How does the Google Search Console integration work?",
|
||||
"Right fit for consultants and agencies?",
|
||||
|
||||
@ -65,10 +65,10 @@ export function WelcomeMessage({
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 space-y-3 pt-0.5 text-sm">
|
||||
<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>
|
||||
To get full access to OpenSEO, you need to upgrade to the paid plan.
|
||||
But, I’m here if you have any questions.
|
||||
To get full access to CrawlerX, you need to upgrade to the paid
|
||||
plan. But, I’m here if you have any questions.
|
||||
</p>
|
||||
<p>
|
||||
You can also{" "}
|
||||
@ -146,7 +146,9 @@ export function UpgradeSidebar({
|
||||
<Globe className="size-4" />
|
||||
</span>
|
||||
<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}>
|
||||
{domain}
|
||||
</p>
|
||||
@ -187,7 +189,7 @@ export function UpgradeSidebar({
|
||||
{isStartingCheckout ? "Redirecting..." : "Upgrade to continue"}
|
||||
</button>
|
||||
<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
|
||||
href={DISCORD_URL}
|
||||
target="_blank"
|
||||
@ -231,7 +233,7 @@ export function ChatGate({
|
||||
That’s all {FREE_ONBOARDING_QUESTION_LIMIT} free questions
|
||||
</p>
|
||||
<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>
|
||||
<button
|
||||
type="button"
|
||||
@ -252,7 +254,7 @@ export function ChatGate({
|
||||
export function ChatComposer({
|
||||
busy,
|
||||
onSend,
|
||||
placeholder = "Ask Sam about your strategy or OpenSEO…",
|
||||
placeholder = "Ask Sam about your strategy or CrawlerX…",
|
||||
}: {
|
||||
busy: boolean;
|
||||
onSend: (text: string) => void;
|
||||
|
||||
@ -61,7 +61,7 @@ export function PostSignupOnboarding({
|
||||
<div className="text-center space-y-3">
|
||||
<img
|
||||
src="/transparent-logo.png"
|
||||
alt="OpenSEO"
|
||||
alt="CrawlerX"
|
||||
className="mx-auto size-10 rounded-lg"
|
||||
/>
|
||||
<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">
|
||||
{title ??
|
||||
(firstName
|
||||
? `Welcome to OpenSEO, ${firstName}!`
|
||||
: "Welcome to OpenSEO!")}
|
||||
? `Welcome to CrawlerX, ${firstName}!`
|
||||
: "Welcome to CrawlerX!")}
|
||||
</h1>
|
||||
<p className="text-sm text-base-content/60">
|
||||
{helperText ?? "A few quick answers to set things up."}
|
||||
@ -116,7 +116,7 @@ export function PostSignupOnboarding({
|
||||
/>
|
||||
) : step === 2 ? (
|
||||
<OnboardingChoiceGroup
|
||||
title="How did you find OpenSEO?"
|
||||
title="How did you find CrawlerX?"
|
||||
options={[...SOURCE_OPTIONS]}
|
||||
selectedValues={answers.source ? [answers.source] : []}
|
||||
onToggle={(source) => updateAnswers({ source })}
|
||||
|
||||
@ -46,8 +46,8 @@ export function SearchConsoleOnboardingStep() {
|
||||
{project ? <GscConnect projectId={project.id} /> : <Checking />}
|
||||
|
||||
<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
|
||||
building it into the OpenSEO app soon too.
|
||||
For now, Search Console data flows through the CrawlerX MCP. We're
|
||||
building it into the CrawlerX app soon too.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ export function SamChat({
|
||||
<div className="space-y-1">
|
||||
<p className="text-lg font-medium">What should we work on?</p>
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -20,10 +20,10 @@ export function SamSetupGate({
|
||||
<div className="max-w-3xl space-y-1.5">
|
||||
<h2 className="text-xl font-semibold">Enable AI Features</h2>
|
||||
<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{" "}
|
||||
<code>OPENROUTER_API_KEY</code> environment variable, restart
|
||||
OpenSEO, then confirm here.
|
||||
CrawlerX, then confirm here.
|
||||
</div>
|
||||
<div className="text-xs text-base-content/50">
|
||||
Step-by-step instructions for every deployment are in the{" "}
|
||||
|
||||
@ -37,7 +37,7 @@ function BetaNotice() {
|
||||
</button>
|
||||
</div>
|
||||
<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.
|
||||
</p>
|
||||
<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" />
|
||||
</button>
|
||||
<Link to="/" className="ml-1 font-semibold text-base-content">
|
||||
OpenSEO
|
||||
<Link
|
||||
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>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -19,7 +19,7 @@ function SeoApiStatusBanners({
|
||||
<div className="alert alert-warning">
|
||||
<AlertTriangle className="size-4 shrink-0" />
|
||||
<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{" "}
|
||||
<Link
|
||||
{...dataforseoHelpLinkOptions}
|
||||
@ -119,7 +119,7 @@ const MissingSeoSetupModal = React.forwardRef<
|
||||
id="dataforseo-setup-description"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
describe("getStandardErrorMessage", () => {
|
||||
it("maps known error codes to standard copy", () => {
|
||||
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> = {
|
||||
UNAUTHENTICATED: "Please sign in and try again.",
|
||||
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:
|
||||
"An active hosted subscription is required before you can use OpenSEO.",
|
||||
"An active hosted subscription is required before you can use CrawlerX.",
|
||||
INSUFFICIENT_CREDITS:
|
||||
"You've run out of credits. Add more credits or upgrade your plan to continue.",
|
||||
FORBIDDEN: "You do not have access to this resource.",
|
||||
|
||||
@ -2,8 +2,8 @@ import * as React from "react";
|
||||
|
||||
export type ThemePreference = "system" | "light" | "dark";
|
||||
|
||||
const LIGHT_THEME_NAME = "openseo";
|
||||
const DARK_THEME_NAME = "openseo-dark";
|
||||
const LIGHT_THEME_NAME = "crawlerx";
|
||||
const DARK_THEME_NAME = "crawlerx-dark";
|
||||
|
||||
const THEME_STORAGE_KEY = "theme-preference";
|
||||
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";
|
||||
|
||||
@theme {
|
||||
--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 {
|
||||
@ -19,26 +38,26 @@
|
||||
exclude: properties;
|
||||
}
|
||||
|
||||
/* Light theme */
|
||||
/* Light theme — CrawlerX / MetatronCube brand (primary #3779b9) */
|
||||
@plugin "daisyui/theme" {
|
||||
name: "openseo";
|
||||
name: "crawlerx";
|
||||
default: true;
|
||||
prefersdark: false;
|
||||
color-scheme: light;
|
||||
--color-base-100: oklch(100% 0 0);
|
||||
--color-base-200: oklch(97% 0 0);
|
||||
--color-base-300: oklch(92% 0 0);
|
||||
--color-base-content: oklch(20% 0 0);
|
||||
--color-primary: oklch(50% 0.12 262);
|
||||
--color-primary-content: oklch(100% 0 0);
|
||||
--color-secondary: oklch(55% 0.15 145);
|
||||
--color-secondary-content: oklch(100% 0 0);
|
||||
--color-accent: oklch(50% 0.12 262);
|
||||
--color-accent-content: oklch(100% 0 0);
|
||||
--color-neutral: oklch(25% 0 0);
|
||||
--color-neutral-content: oklch(100% 0 0);
|
||||
--color-info: oklch(70% 0.15 230);
|
||||
--color-info-content: oklch(100% 0 0);
|
||||
--color-base-100: #ffffff;
|
||||
--color-base-200: #f6f8fa;
|
||||
--color-base-300: #e6eaef;
|
||||
--color-base-content: #151515;
|
||||
--color-primary: #3779b9;
|
||||
--color-primary-content: #ffffff;
|
||||
--color-secondary: #0f1d34;
|
||||
--color-secondary-content: #ffffff;
|
||||
--color-accent: #4faede;
|
||||
--color-accent-content: #06263a;
|
||||
--color-neutral: #151515;
|
||||
--color-neutral-content: #ffffff;
|
||||
--color-info: #3779b9;
|
||||
--color-info-content: #ffffff;
|
||||
--color-success: oklch(65% 0.18 145);
|
||||
--color-success-content: oklch(100% 0 0);
|
||||
--color-warning: oklch(80% 0.15 80);
|
||||
@ -57,23 +76,23 @@
|
||||
|
||||
/* Dark theme - auto-activates via prefers-color-scheme */
|
||||
@plugin "daisyui/theme" {
|
||||
name: "openseo-dark";
|
||||
name: "crawlerx-dark";
|
||||
prefersdark: true;
|
||||
color-scheme: dark;
|
||||
--color-base-100: oklch(18% 0 0);
|
||||
--color-base-200: oklch(12% 0 0);
|
||||
--color-base-300: oklch(27% 0 0);
|
||||
--color-base-content: oklch(92% 0 0);
|
||||
--color-primary: oklch(66% 0.12 262);
|
||||
--color-primary-content: oklch(100% 0 0);
|
||||
--color-secondary: oklch(60% 0.15 145);
|
||||
--color-secondary-content: oklch(100% 0 0);
|
||||
--color-accent: oklch(66% 0.12 262);
|
||||
--color-accent-content: oklch(100% 0 0);
|
||||
--color-neutral: oklch(85% 0 0);
|
||||
--color-neutral-content: oklch(20% 0 0);
|
||||
--color-info: oklch(70% 0.15 230);
|
||||
--color-info-content: oklch(100% 0 0);
|
||||
--color-base-100: #141821;
|
||||
--color-base-200: #0e121b;
|
||||
--color-base-300: #232a37;
|
||||
--color-base-content: #e8ebf0;
|
||||
--color-primary: #3f8ad4;
|
||||
--color-primary-content: #ffffff;
|
||||
--color-secondary: #9fb3c8;
|
||||
--color-secondary-content: #0e121b;
|
||||
--color-accent: #4faede;
|
||||
--color-accent-content: #06263a;
|
||||
--color-neutral: #e8ebf0;
|
||||
--color-neutral-content: #141821;
|
||||
--color-info: #3f8ad4;
|
||||
--color-info-content: #ffffff;
|
||||
--color-success: oklch(65% 0.18 145);
|
||||
--color-success-content: oklch(100% 0 0);
|
||||
--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,
|
||||
so they read as sunken/disabled. Colored variants (primary, error, ...)
|
||||
set their own --btn-color and are excluded. */
|
||||
html[data-theme="openseo-dark"]
|
||||
html[data-theme="crawlerx-dark"]
|
||||
.btn:not(
|
||||
.btn-primary,
|
||||
.btn-error,
|
||||
@ -168,7 +187,7 @@ html[data-theme="openseo-dark"]
|
||||
/* Light mode: the base-200 default button is nearly invisible on the
|
||||
base-100 content panel; use a white surface with a visible border,
|
||||
matching inputs/selects. */
|
||||
html[data-theme="openseo"]
|
||||
html[data-theme="crawlerx"]
|
||||
.btn:not(
|
||||
.btn-primary,
|
||||
.btn-error,
|
||||
@ -251,28 +270,28 @@ html[data-theme="openseo"]
|
||||
--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;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .tag-chip-rose {
|
||||
html[data-theme="crawlerx-dark"] .tag-chip-rose {
|
||||
color: #fda4af;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .tag-chip-amber {
|
||||
html[data-theme="crawlerx-dark"] .tag-chip-amber {
|
||||
color: #fcd34d;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .tag-chip-lime {
|
||||
html[data-theme="crawlerx-dark"] .tag-chip-lime {
|
||||
color: #d9f99d;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .tag-chip-emerald {
|
||||
html[data-theme="crawlerx-dark"] .tag-chip-emerald {
|
||||
color: #6ee7b7;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .tag-chip-sky {
|
||||
html[data-theme="crawlerx-dark"] .tag-chip-sky {
|
||||
color: #7dd3fc;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .tag-chip-violet {
|
||||
html[data-theme="crawlerx-dark"] .tag-chip-violet {
|
||||
color: #c4b5fd;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .tag-chip-fuchsia {
|
||||
html[data-theme="crawlerx-dark"] .tag-chip-fuchsia {
|
||||
color: #f5d0fe;
|
||||
}
|
||||
|
||||
@ -323,29 +342,29 @@ html[data-theme="openseo-dark"] .tag-chip-fuchsia {
|
||||
--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;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .score-tier-1 {
|
||||
html[data-theme="crawlerx-dark"] .score-tier-1 {
|
||||
color: #6ee7b7;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .score-tier-2 {
|
||||
html[data-theme="crawlerx-dark"] .score-tier-2 {
|
||||
color: #bef264;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .score-tier-3 {
|
||||
html[data-theme="crawlerx-dark"] .score-tier-3 {
|
||||
color: #fde047;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .score-tier-4 {
|
||||
html[data-theme="crawlerx-dark"] .score-tier-4 {
|
||||
color: #fdba74;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .score-tier-5 {
|
||||
html[data-theme="crawlerx-dark"] .score-tier-5 {
|
||||
color: #fca5a5;
|
||||
}
|
||||
html[data-theme="openseo-dark"] .score-tier-6 {
|
||||
html[data-theme="crawlerx-dark"] .score-tier-6 {
|
||||
color: #fda4af;
|
||||
}
|
||||
|
||||
html[data-theme="openseo-dark"] {
|
||||
html[data-theme="crawlerx-dark"] {
|
||||
--trend-grid-color: oklch(78% 0.01 255 / 0.22);
|
||||
--trend-axis-color: oklch(84% 0.01 255 / 0.62);
|
||||
--trend-fill-start-opacity: 0.24;
|
||||
@ -355,18 +374,18 @@ html[data-theme="openseo-dark"] {
|
||||
--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;
|
||||
}
|
||||
|
||||
html[data-theme="openseo-dark"] .alert-info {
|
||||
html[data-theme="crawlerx-dark"] .alert-info {
|
||||
@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;
|
||||
}
|
||||
|
||||
html[data-theme="openseo-dark"] .alert-error {
|
||||
html[data-theme="crawlerx-dark"] .alert-error {
|
||||
@apply border-error/60 bg-error/20;
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export const Route = createRootRoute({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
title: "OpenSEO",
|
||||
title: "CrawlerX",
|
||||
},
|
||||
{
|
||||
charSet: "utf-8",
|
||||
|
||||
@ -53,8 +53,9 @@ function AiPage() {
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<h1 className="text-2xl font-semibold">AI & MCP</h1>
|
||||
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
||||
Connect your AI agent to OpenSEO. Run keyword research, SERP analysis,
|
||||
domain lookups, and backlink reviews from your editor or chat.
|
||||
Connect your AI agent to CrawlerX. Run keyword research, SERP
|
||||
analysis, domain lookups, and backlink reviews from your editor or
|
||||
chat.
|
||||
</p>
|
||||
|
||||
{getAuthMode(import.meta.env.AUTH_MODE) === "cloudflare_access" ? (
|
||||
@ -92,9 +93,9 @@ function AiPage() {
|
||||
</code>
|
||||
</div>
|
||||
<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.
|
||||
Sign in with OpenSEO when prompted.
|
||||
Sign in with CrawlerX when prompted.
|
||||
</p>
|
||||
{isHostedClientAuthMode() ? (
|
||||
<p className="mt-2 text-xs text-base-content/55">
|
||||
@ -154,9 +155,9 @@ function AiPage() {
|
||||
.
|
||||
</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>
|
||||
Optional: after OpenSEO connects, click{" "}
|
||||
Optional: after CrawlerX connects, click{" "}
|
||||
<span className="font-medium text-base-content">
|
||||
Configure
|
||||
</span>
|
||||
@ -216,17 +217,17 @@ function AiPage() {
|
||||
.
|
||||
</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>
|
||||
</Collapsible>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<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">
|
||||
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.
|
||||
</p>
|
||||
<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} />
|
||||
<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>
|
||||
<CodeBlock code={ALL_SKILLS_INSTALL} />
|
||||
</Collapsible>
|
||||
@ -320,7 +321,7 @@ function AiPage() {
|
||||
{
|
||||
title: "In-app SEO Research Agent",
|
||||
description:
|
||||
"Ask questions and run research without leaving OpenSEO",
|
||||
"Ask questions and run research without leaving CrawlerX",
|
||||
},
|
||||
{
|
||||
title: "Content Assistant",
|
||||
|
||||
@ -196,7 +196,7 @@ function BillingPage() {
|
||||
</div>
|
||||
<ul className="space-y-1.5">
|
||||
{[
|
||||
"Access to all OpenSEO features",
|
||||
"Access to all CrawlerX features",
|
||||
"Includes $10.00 of Usage Credits each month",
|
||||
].map((item) => (
|
||||
<li
|
||||
|
||||
@ -16,7 +16,7 @@ function DataforseoApiKeyHelpPage() {
|
||||
Set up your DataForSEO API key
|
||||
</h1>
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
@ -62,7 +62,7 @@ function DataforseoApiKeyHelpPage() {
|
||||
<li>
|
||||
In Cloudflare, go to <code>Compute</code> ->{" "}
|
||||
<code>Workers & Pages</code>
|
||||
and open your OpenSEO Worker.
|
||||
and open your CrawlerX Worker.
|
||||
</li>
|
||||
<li>
|
||||
Open <code>Settings</code>.
|
||||
|
||||
@ -16,9 +16,9 @@ function OpenrouterApiKeyHelpPage() {
|
||||
Set up your OpenRouter API key
|
||||
</h1>
|
||||
<p className="text-sm text-base-content/70">
|
||||
OpenSEO needs the <code>OPENROUTER_API_KEY</code> secret before AI
|
||||
features like SAM, the in-app SEO agent, can run. It is optional —
|
||||
everything else in OpenSEO works without it.
|
||||
CrawlerX needs the <code>OPENROUTER_API_KEY</code> secret before
|
||||
AI features like SAM, the in-app SEO agent, can run. It is
|
||||
optional — everything else in CrawlerX works without it.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -64,7 +64,7 @@ function OpenrouterApiKeyHelpPage() {
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Restart OpenSEO.</li>
|
||||
<li>Restart CrawlerX.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
@ -78,7 +78,7 @@ function OpenrouterApiKeyHelpPage() {
|
||||
<li>
|
||||
In Cloudflare, go to <code>Compute</code> ->{" "}
|
||||
<code>Workers & Pages</code>
|
||||
and open your OpenSEO Worker.
|
||||
and open your CrawlerX Worker.
|
||||
</li>
|
||||
<li>
|
||||
Open <code>Settings</code>.
|
||||
|
||||
@ -76,7 +76,7 @@ function IndexRedirect() {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full p-4">
|
||||
<UnauthenticatedErrorCard
|
||||
message="Please sign in to access your OpenSEO organization."
|
||||
message="Please sign in to access your CrawlerX organization."
|
||||
onRetry={() => {
|
||||
void refetch();
|
||||
}}
|
||||
|
||||
@ -95,7 +95,7 @@ function PersonalSettings() {
|
||||
</h2>
|
||||
<div className="flex items-start justify-between gap-6">
|
||||
<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">
|
||||
Share analytics and usage data.
|
||||
</p>
|
||||
|
||||
@ -32,7 +32,7 @@ function SupportPage() {
|
||||
</h1>
|
||||
<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
|
||||
how you work so we can make OpenSEO better.
|
||||
how you work so we can make CrawlerX better.
|
||||
</p>
|
||||
|
||||
<div className="mt-8 space-y-3">
|
||||
|
||||
@ -87,7 +87,7 @@ function SignUpPage() {
|
||||
redirect_to: redirectTo,
|
||||
});
|
||||
const resolvedName =
|
||||
value.name.trim() || email.split("@")[0] || "OpenSEO User";
|
||||
value.name.trim() || email.split("@")[0] || "CrawlerX User";
|
||||
const verificationCallbackURL = new URL(
|
||||
"/verify-email",
|
||||
window.location.origin,
|
||||
|
||||
@ -11,7 +11,7 @@ export const Route = createFileRoute("/_authenticated/oauth-consent")({
|
||||
const SCOPES = [
|
||||
{
|
||||
icon: Database,
|
||||
label: "Read your OpenSEO data",
|
||||
label: "Read your CrawlerX data",
|
||||
description: "Projects, keyword reports, and audit results.",
|
||||
},
|
||||
{
|
||||
@ -74,12 +74,12 @@ function OAuthConsentPage() {
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<img
|
||||
src="/transparent-logo.png"
|
||||
alt="OpenSEO"
|
||||
alt="CrawlerX"
|
||||
className="size-10 rounded-lg"
|
||||
/>
|
||||
<h1 className="mt-5 text-xl font-semibold">Authorize MCP access</h1>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ function SubscribePage() {
|
||||
<div className="w-full max-w-xs space-y-4 text-center">
|
||||
<img
|
||||
src="/transparent-logo.png"
|
||||
alt="OpenSEO"
|
||||
alt="CrawlerX"
|
||||
className="mx-auto size-10 rounded-lg"
|
||||
/>
|
||||
<h1 className="text-xl font-semibold">
|
||||
@ -163,7 +163,7 @@ function SubscribePage() {
|
||||
<div className="text-center space-y-3">
|
||||
<img
|
||||
src="/transparent-logo.png"
|
||||
alt="OpenSEO"
|
||||
alt="CrawlerX"
|
||||
className="mx-auto size-10 rounded-lg"
|
||||
/>
|
||||
<h1 className="text-xl font-semibold">Billing unavailable</h1>
|
||||
@ -223,15 +223,15 @@ function SubscribePage() {
|
||||
<div className="text-center space-y-3">
|
||||
<img
|
||||
src="/transparent-logo.png"
|
||||
alt="OpenSEO"
|
||||
alt="CrawlerX"
|
||||
className="mx-auto size-10 rounded-lg"
|
||||
/>
|
||||
<h1 className="text-xl font-semibold">
|
||||
{isUpgradeFlow
|
||||
? "Upgrade your plan"
|
||||
: firstName
|
||||
? `Welcome to OpenSEO, ${firstName}!`
|
||||
: "Welcome to OpenSEO!"}
|
||||
? `Welcome to CrawlerX, ${firstName}!`
|
||||
: "Welcome to CrawlerX!"}
|
||||
</h1>
|
||||
<p className="text-sm text-base-content/60">
|
||||
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 (
|
||||
<AuthPageCard title="You’re invited">
|
||||
<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.
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
@ -212,7 +212,7 @@ function InvitationCard({
|
||||
<span className="font-medium">
|
||||
{invitationQuery.data.organizationName}
|
||||
</span>{" "}
|
||||
on OpenSEO.
|
||||
on CrawlerX.
|
||||
</p>
|
||||
{actionError ? <p className="text-sm text-error">{actionError}</p> : null}
|
||||
<div className="space-y-2">
|
||||
|
||||
@ -39,7 +39,7 @@ function AuthErrorPage() {
|
||||
}
|
||||
>
|
||||
<Link to="/" className="btn btn-soft w-full">
|
||||
Back to OpenSEO
|
||||
Back to CrawlerX
|
||||
</Link>
|
||||
</AuthPageCard>
|
||||
</AuthPageShell>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user