@lukoweb/apitogo 0.1.87 → 0.1.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cli.js +112 -81
- package/dist/declarations/config/landing-manifest.d.ts +5 -0
- package/dist/declarations/config/local-manifest.d.ts +5 -0
- package/dist/declarations/config/validators/ModulesSchema.d.ts +21 -0
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +5 -0
- package/dist/flat-config.d.ts +2 -0
- package/package.json +1 -1
- package/src/app/apitogoBrandTheme.ts +30 -29
- package/src/app/defaultTheme.css +56 -56
- package/src/config/validators/ModulesSchema.ts +2 -0
- package/src/lib/authentication/providers/dev-portal-auth-pages.tsx +45 -18
- package/src/lib/components/Header.tsx +8 -5
- package/src/lib/components/HeaderAuthActions.tsx +5 -7
- package/src/lib/components/Layout.tsx +13 -4
- package/src/lib/components/Main.tsx +3 -2
- package/src/lib/components/MobileTopNavigation.tsx +2 -5
- package/src/lib/components/ThemeSwitch.tsx +41 -25
package/dist/cli/cli.js
CHANGED
|
@@ -815,7 +815,8 @@ var init_ModulesSchema = __esm({
|
|
|
815
815
|
path: z3.string(),
|
|
816
816
|
icon: z3.string().optional(),
|
|
817
817
|
latency: z3.string().optional(),
|
|
818
|
-
summary: z3.string().optional()
|
|
818
|
+
summary: z3.string().optional(),
|
|
819
|
+
method: z3.string().optional()
|
|
819
820
|
});
|
|
820
821
|
LandingApiShowcaseSchema = z3.object({
|
|
821
822
|
enabled: z3.boolean().optional(),
|
|
@@ -824,6 +825,7 @@ var init_ModulesSchema = __esm({
|
|
|
824
825
|
browseAllHref: z3.string().optional(),
|
|
825
826
|
browseAllLabel: z3.string().optional(),
|
|
826
827
|
autoFromOpenApi: z3.boolean().optional(),
|
|
828
|
+
layout: z3.enum(["grid", "list"]).optional(),
|
|
827
829
|
items: z3.array(LandingApiShowcaseItemSchema).optional()
|
|
828
830
|
}).optional();
|
|
829
831
|
LandingPricingCtaSchema = z3.object({
|
|
@@ -6257,7 +6259,7 @@ var SCAFFOLD_SUGGESTED_NEXT_STEPS = [
|
|
|
6257
6259
|
prompt: "Design your home page?",
|
|
6258
6260
|
tool: "get_dev_portal_landing_guide",
|
|
6259
6261
|
optional: true,
|
|
6260
|
-
note: "Ask the user for their real content (product, endpoints, audience, tone, sections) before editing landing/LandingPage.tsx \u2014 compose HeroSection, TrustSection, FeaturesSection, ApiShowcaseSection, and PricingCtaSection inside LandingShell."
|
|
6262
|
+
note: "Ask the user for their real content (product, endpoints, audience, tone, sections) before editing landing/LandingPage.tsx \u2014 compose HeroSection, TrustSection, FeaturesSection, StepsSection, ApiShowcaseSection, PlansPreviewSection, and PricingCtaSection inside LandingShell."
|
|
6261
6263
|
},
|
|
6262
6264
|
{
|
|
6263
6265
|
id: "addPlans",
|
|
@@ -12491,7 +12493,7 @@ import {
|
|
|
12491
12493
|
// package.json
|
|
12492
12494
|
var package_default = {
|
|
12493
12495
|
name: "@lukoweb/apitogo",
|
|
12494
|
-
version: "0.1.
|
|
12496
|
+
version: "0.1.88",
|
|
12495
12497
|
type: "module",
|
|
12496
12498
|
sideEffects: [
|
|
12497
12499
|
"**/*.css",
|
|
@@ -18428,8 +18430,7 @@ async function resolveCreateApitogoInvocation(options) {
|
|
|
18428
18430
|
return {
|
|
18429
18431
|
source: "npm",
|
|
18430
18432
|
command: "npx",
|
|
18431
|
-
args: [pkg]
|
|
18432
|
-
warning: `Using ${pkg} via npx (downloads on first run if not cached). Set APITOGO_DOC_TOOL_PATH to scaffold from a local apitogo-doc-tool checkout.`
|
|
18433
|
+
args: [pkg]
|
|
18433
18434
|
};
|
|
18434
18435
|
}
|
|
18435
18436
|
function runCommand2(command, args, cwd) {
|
|
@@ -19000,44 +19001,52 @@ function createDefaultApitogoJsonConfig(options) {
|
|
|
19000
19001
|
},
|
|
19001
19002
|
theme: {
|
|
19002
19003
|
fonts: {
|
|
19003
|
-
sans: "
|
|
19004
|
+
sans: "Inter",
|
|
19004
19005
|
mono: "JetBrains Mono"
|
|
19005
19006
|
},
|
|
19006
19007
|
light: {
|
|
19007
|
-
background: "#
|
|
19008
|
-
foreground: "#
|
|
19009
|
-
card: "#
|
|
19010
|
-
cardForeground: "#
|
|
19011
|
-
primary: "#
|
|
19008
|
+
background: "#ffffff",
|
|
19009
|
+
foreground: "#252b3b",
|
|
19010
|
+
card: "#ffffff",
|
|
19011
|
+
cardForeground: "#252b3b",
|
|
19012
|
+
primary: "#0d9488",
|
|
19012
19013
|
primaryForeground: "#ffffff",
|
|
19013
|
-
secondary: "#
|
|
19014
|
-
secondaryForeground: "#
|
|
19015
|
-
muted: "#
|
|
19016
|
-
mutedForeground: "
|
|
19017
|
-
accent: "rgba(
|
|
19018
|
-
accentForeground: "#
|
|
19019
|
-
border: "#
|
|
19020
|
-
input: "#
|
|
19021
|
-
ring: "#
|
|
19014
|
+
secondary: "#f4f6fb",
|
|
19015
|
+
secondaryForeground: "#252b3b",
|
|
19016
|
+
muted: "#e9edf5",
|
|
19017
|
+
mutedForeground: "rgba(37, 43, 59, 0.55)",
|
|
19018
|
+
accent: "rgba(13, 148, 136, 0.1)",
|
|
19019
|
+
accentForeground: "#0d9488",
|
|
19020
|
+
border: "#d9deea",
|
|
19021
|
+
input: "#d9deea",
|
|
19022
|
+
ring: "#0d9488"
|
|
19022
19023
|
},
|
|
19023
19024
|
dark: {
|
|
19024
|
-
background: "#
|
|
19025
|
-
foreground: "#
|
|
19026
|
-
card: "#
|
|
19027
|
-
cardForeground: "#
|
|
19028
|
-
primary: "#
|
|
19025
|
+
background: "#252b3b",
|
|
19026
|
+
foreground: "#e9edf5",
|
|
19027
|
+
card: "#2b3243",
|
|
19028
|
+
cardForeground: "#e9edf5",
|
|
19029
|
+
primary: "#0d9488",
|
|
19029
19030
|
primaryForeground: "#ffffff",
|
|
19030
|
-
secondary: "#
|
|
19031
|
-
secondaryForeground: "#
|
|
19032
|
-
muted: "#
|
|
19033
|
-
mutedForeground: "
|
|
19034
|
-
accent: "rgba(
|
|
19035
|
-
accentForeground: "#
|
|
19036
|
-
border: "
|
|
19037
|
-
input: "#
|
|
19038
|
-
ring: "#
|
|
19031
|
+
secondary: "#1e2330",
|
|
19032
|
+
secondaryForeground: "#e9edf5",
|
|
19033
|
+
muted: "#1e2330",
|
|
19034
|
+
mutedForeground: "rgba(217, 222, 234, 0.58)",
|
|
19035
|
+
accent: "rgba(13, 148, 136, 0.12)",
|
|
19036
|
+
accentForeground: "#0d9488",
|
|
19037
|
+
border: "rgba(217, 222, 234, 0.18)",
|
|
19038
|
+
input: "#323a4f",
|
|
19039
|
+
ring: "#0d9488"
|
|
19039
19040
|
}
|
|
19040
19041
|
},
|
|
19042
|
+
header: {
|
|
19043
|
+
placements: { navigation: "center" },
|
|
19044
|
+
navigation: [
|
|
19045
|
+
{ label: "Product", to: "/#features" },
|
|
19046
|
+
{ label: "Pricing", to: "/pricing" },
|
|
19047
|
+
{ label: "Docs", to: "/introduction" }
|
|
19048
|
+
]
|
|
19049
|
+
},
|
|
19041
19050
|
modules: {
|
|
19042
19051
|
docs: {
|
|
19043
19052
|
files: filesGlob
|
|
@@ -19046,15 +19055,80 @@ function createDefaultApitogoJsonConfig(options) {
|
|
|
19046
19055
|
enabled: true,
|
|
19047
19056
|
path: "/",
|
|
19048
19057
|
layout: "landing",
|
|
19049
|
-
page: "./landing/LandingPage.tsx"
|
|
19058
|
+
page: "./landing/LandingPage.tsx",
|
|
19059
|
+
content: {
|
|
19060
|
+
footer: {
|
|
19061
|
+
brand: {
|
|
19062
|
+
name: "SampleAPI",
|
|
19063
|
+
tagline: "A hosted sandbox API for prototypes, demos, and QA \u2014 realistic data, real auth, real limits."
|
|
19064
|
+
},
|
|
19065
|
+
columns: [
|
|
19066
|
+
{
|
|
19067
|
+
title: "Product",
|
|
19068
|
+
links: [
|
|
19069
|
+
{ label: "Overview", href: "/#features" },
|
|
19070
|
+
{ label: "Pricing", href: "/pricing" },
|
|
19071
|
+
{ label: "Documentation", href: "/introduction" }
|
|
19072
|
+
]
|
|
19073
|
+
},
|
|
19074
|
+
{
|
|
19075
|
+
title: "Account",
|
|
19076
|
+
links: [
|
|
19077
|
+
{ label: "Log in", href: "/signin" },
|
|
19078
|
+
{ label: "Get started", href: "/signup" },
|
|
19079
|
+
{ label: "Dashboard", href: "/account/dashboard" }
|
|
19080
|
+
]
|
|
19081
|
+
},
|
|
19082
|
+
{
|
|
19083
|
+
title: "Company",
|
|
19084
|
+
links: [
|
|
19085
|
+
{ label: "Contact", href: "#" },
|
|
19086
|
+
{ label: "Terms", href: "#" },
|
|
19087
|
+
{ label: "Privacy", href: "#" }
|
|
19088
|
+
]
|
|
19089
|
+
}
|
|
19090
|
+
],
|
|
19091
|
+
copyright: "\xA9 2026 Sample API"
|
|
19092
|
+
}
|
|
19093
|
+
}
|
|
19050
19094
|
},
|
|
19051
19095
|
userPanel: {
|
|
19052
19096
|
enabled: true,
|
|
19053
19097
|
dashboard: { enabled: true },
|
|
19054
19098
|
userManagement: { enabled: true },
|
|
19055
19099
|
plans: {
|
|
19056
|
-
enabled:
|
|
19057
|
-
page: "./user-panel/AccountPlansPage.tsx"
|
|
19100
|
+
enabled: true,
|
|
19101
|
+
page: "./user-panel/AccountPlansPage.tsx",
|
|
19102
|
+
items: [
|
|
19103
|
+
{
|
|
19104
|
+
sku: "free",
|
|
19105
|
+
displayName: "Free",
|
|
19106
|
+
priceAmount: 0,
|
|
19107
|
+
billingPeriod: "month",
|
|
19108
|
+
limitsJson: '{"rateLimit":{"calls":10,"renewalPeriodSeconds":1},"quota":{"calls":1000,"renewalPeriodDays":30}}'
|
|
19109
|
+
},
|
|
19110
|
+
{
|
|
19111
|
+
sku: "developer",
|
|
19112
|
+
displayName: "Developer",
|
|
19113
|
+
priceAmount: 19,
|
|
19114
|
+
billingPeriod: "month",
|
|
19115
|
+
limitsJson: '{"rateLimit":{"calls":50,"renewalPeriodSeconds":1},"quota":{"calls":50000,"renewalPeriodDays":30}}'
|
|
19116
|
+
},
|
|
19117
|
+
{
|
|
19118
|
+
sku: "team",
|
|
19119
|
+
displayName: "Team",
|
|
19120
|
+
priceAmount: 79,
|
|
19121
|
+
billingPeriod: "month",
|
|
19122
|
+
limitsJson: '{"rateLimit":{"calls":200,"renewalPeriodSeconds":1},"quota":{"calls":500000,"renewalPeriodDays":30}}'
|
|
19123
|
+
},
|
|
19124
|
+
{
|
|
19125
|
+
sku: "enterprise",
|
|
19126
|
+
displayName: "Enterprise",
|
|
19127
|
+
priceAmount: -1,
|
|
19128
|
+
billingPeriod: "custom",
|
|
19129
|
+
limitsJson: '{"quota":{"calls":-1}}'
|
|
19130
|
+
}
|
|
19131
|
+
]
|
|
19058
19132
|
}
|
|
19059
19133
|
}
|
|
19060
19134
|
}
|
|
@@ -19842,50 +19916,7 @@ async function ensureTsconfigJson(dir) {
|
|
|
19842
19916
|
return true;
|
|
19843
19917
|
}
|
|
19844
19918
|
}
|
|
19845
|
-
var LANDING_PAGE_SOURCE =
|
|
19846
|
-
FeaturesSection,
|
|
19847
|
-
HeroSection,
|
|
19848
|
-
LandingShell,
|
|
19849
|
-
} from "@lukoweb/apitogo-module-landing";
|
|
19850
|
-
|
|
19851
|
-
export default function LandingPage() {
|
|
19852
|
-
return (
|
|
19853
|
-
<LandingShell
|
|
19854
|
-
title="Your API, ready for developers"
|
|
19855
|
-
description="Documentation, API reference, and a developer portal \u2014 all in one place."
|
|
19856
|
-
>
|
|
19857
|
-
<HeroSection
|
|
19858
|
-
hero={{
|
|
19859
|
-
title: "Your API, ready for developers",
|
|
19860
|
-
description:
|
|
19861
|
-
"Ship docs, interactive API reference, and a developer portal without building from scratch.",
|
|
19862
|
-
}}
|
|
19863
|
-
primaryAction={{ label: "Get started", href: "/introduction" }}
|
|
19864
|
-
secondaryAction={{ label: "API Reference", href: "/api" }}
|
|
19865
|
-
/>
|
|
19866
|
-
<FeaturesSection
|
|
19867
|
-
features={[
|
|
19868
|
-
{
|
|
19869
|
-
title: "Documentation",
|
|
19870
|
-
description: "Guides and references for every integration path.",
|
|
19871
|
-
icon: "book-open",
|
|
19872
|
-
},
|
|
19873
|
-
{
|
|
19874
|
-
title: "API Reference",
|
|
19875
|
-
description: "Interactive OpenAPI explorer with code samples.",
|
|
19876
|
-
icon: "code",
|
|
19877
|
-
},
|
|
19878
|
-
{
|
|
19879
|
-
title: "Developer Portal",
|
|
19880
|
-
description: "Auth, API keys, and subscription billing built in.",
|
|
19881
|
-
icon: "shield-check",
|
|
19882
|
-
},
|
|
19883
|
-
]}
|
|
19884
|
-
/>
|
|
19885
|
-
</LandingShell>
|
|
19886
|
-
);
|
|
19887
|
-
}
|
|
19888
|
-
`;
|
|
19919
|
+
var LANDING_PAGE_SOURCE = 'import {\n ApiShowcaseSection,\n FeaturesSection,\n HeroSection,\n LandingShell,\n PlansPreviewSection,\n PricingCtaSection,\n StepsSection,\n} from "@lukoweb/apitogo-module-landing";\n\nexport default function LandingPage() {\n return (\n <LandingShell\n title="Test against a real API before you build one"\n description="Sample API gives you realistic endpoints, authentication, rate limits, and webhooks \u2014 a fully working backend for prototypes, demos, and QA."\n >\n <HeroSection\n hero={{\n badge: "SANDBOX API",\n title: "Test against a real API before you build one",\n description:\n "Sample API gives you realistic endpoints, authentication, rate limits, and webhooks \u2014 a fully working backend for prototypes, demos, and QA. No setup, no infrastructure.",\n }}\n primaryAction={{ label: "Start for free", href: "/signup" }}\n secondaryAction={{ label: "Read the docs", href: "/introduction" }}\n codeExamples={[\n {\n filename: "Create a key",\n language: "bash",\n code: `$ curl -X POST https://api.sampleapi.dev/v1/keys\n# sk_live_9f2c7a1e...`,\n },\n {\n filename: "Call an endpoint",\n language: "bash",\n code: `$ curl https://api.sampleapi.dev/v1/users/42 \\\\\n -H "Authorization: Bearer sk_live_9f2c..."\n# { "id": 42, "name": "Priya Shah", "plan": "pro" }`,\n },\n ]}\n />\n <FeaturesSection\n section={{\n label: "Everything a real API has",\n title: "Built for testing, not just browsing",\n description:\n "Realistic data, live auth, rate limits, and webhooks \u2014 so you can ship with confidence.",\n }}\n features={[\n {\n title: "Realistic mock data",\n description:\n "Users, orders, products, and posts with relationships and pagination, not lorem ipsum.",\n icon: "boxes",\n },\n {\n title: "Real auth flow",\n description:\n "Every request checks a live API key, so you can test error handling and expiry.",\n icon: "shield-check",\n },\n {\n title: "Rate limiting",\n description:\n "Per-plan request limits and 429 responses, so you can build and test backoff logic.",\n icon: "gauge",\n },\n {\n title: "Webhooks",\n description:\n "Trigger test events and inspect delivered payloads and retries.",\n icon: "webhook",\n },\n {\n title: "OpenAPI & docs",\n description:\n "A full OpenAPI spec and reference docs for every endpoint.",\n icon: "book-open",\n },\n {\n title: "Usage analytics",\n description:\n "Track requests, latency, and errors per key from your dashboard.",\n icon: "activity",\n },\n ]}\n />\n <StepsSection\n steps={[\n {\n step: "01",\n title: "Create an account",\n description:\n "Sign up and land straight in your dashboard \u2014 no approval queue.",\n },\n {\n step: "02",\n title: "Generate a key",\n description:\n "Every key is scoped and rate-limited to your plan from the moment it\'s created.",\n },\n {\n step: "03",\n title: "Call any endpoint",\n description:\n "Users, orders, products, posts, and webhooks \u2014 all documented, all live.",\n },\n ]}\n />\n <ApiShowcaseSection\n apiShowcase={{\n title: "Sample endpoints",\n subtitle:\n "A slice of what\'s available \u2014 see the full reference in the docs.",\n browseAllHref: "/api",\n browseAllLabel: "Browse all APIs",\n layout: "list",\n items: [\n {\n name: "List paginated users",\n path: "/v1/users",\n method: "GET",\n summary: "List paginated users",\n },\n {\n name: "Create a mock order",\n path: "/v1/orders",\n method: "POST",\n summary: "Create a mock order",\n },\n {\n name: "Fetch one product",\n path: "/v1/products/{id}",\n method: "GET",\n summary: "Fetch one product",\n },\n {\n name: "List posts with comments",\n path: "/v1/posts",\n method: "GET",\n summary: "List posts with comments",\n },\n {\n name: "Fire a test webhook event",\n path: "/v1/webhooks/test",\n method: "POST",\n summary: "Fire a test webhook event",\n },\n {\n name: "Delete a mock order",\n path: "/v1/orders/{id}",\n method: "DELETE",\n summary: "Delete a mock order",\n },\n ],\n }}\n />\n <PlansPreviewSection\n plans={[\n {\n name: "Free",\n price: "$0",\n unit: "per month",\n blurb: "1,000 requests/mo, community support.",\n },\n {\n name: "Developer",\n price: "$19",\n unit: "per month",\n blurb: "50,000 requests/mo, webhooks included.",\n featured: true,\n },\n {\n name: "Team",\n price: "$79",\n unit: "per month",\n blurb: "500,000 requests/mo, 5 team seats.",\n },\n {\n name: "Enterprise",\n price: "Custom",\n unit: "talk to us",\n blurb: "Unlimited requests, SSO, SLA.",\n },\n ]}\n />\n <PricingCtaSection\n variant="banner"\n pricingCta={{\n title: "Start testing in minutes",\n description:\n "Free tier includes 1,000 requests a month \u2014 no card required.",\n primaryAction: { label: "Start for free", href: "/signup" },\n }}\n />\n </LandingShell>\n );\n}\n';
|
|
19889
19920
|
var ACCOUNT_PLANS_PAGE_SOURCE = `export { AccountPlansPage as default } from "@lukoweb/apitogo-plugin-dev-portal-billing";
|
|
19890
19921
|
`;
|
|
19891
19922
|
var fileExists2 = async (filePath) => {
|
|
@@ -44,12 +44,17 @@ export declare const LandingManifestContentSchema: z.ZodOptional<z.ZodObject<{
|
|
|
44
44
|
browseAllHref: z.ZodOptional<z.ZodString>;
|
|
45
45
|
browseAllLabel: z.ZodOptional<z.ZodString>;
|
|
46
46
|
autoFromOpenApi: z.ZodOptional<z.ZodBoolean>;
|
|
47
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
48
|
+
grid: "grid";
|
|
49
|
+
list: "list";
|
|
50
|
+
}>>;
|
|
47
51
|
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
48
52
|
name: z.ZodString;
|
|
49
53
|
path: z.ZodString;
|
|
50
54
|
icon: z.ZodOptional<z.ZodString>;
|
|
51
55
|
latency: z.ZodOptional<z.ZodString>;
|
|
52
56
|
summary: z.ZodOptional<z.ZodString>;
|
|
57
|
+
method: z.ZodOptional<z.ZodString>;
|
|
53
58
|
}, z.core.$strip>>>;
|
|
54
59
|
}, z.core.$strip>>;
|
|
55
60
|
pricingCta: z.ZodOptional<z.ZodObject<{
|
|
@@ -137,12 +137,17 @@ export declare const DevPortalLocalManifestSchema: z.ZodObject<{
|
|
|
137
137
|
browseAllHref: z.ZodOptional<z.ZodString>;
|
|
138
138
|
browseAllLabel: z.ZodOptional<z.ZodString>;
|
|
139
139
|
autoFromOpenApi: z.ZodOptional<z.ZodBoolean>;
|
|
140
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
141
|
+
grid: "grid";
|
|
142
|
+
list: "list";
|
|
143
|
+
}>>;
|
|
140
144
|
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
141
145
|
name: z.ZodString;
|
|
142
146
|
path: z.ZodString;
|
|
143
147
|
icon: z.ZodOptional<z.ZodString>;
|
|
144
148
|
latency: z.ZodOptional<z.ZodString>;
|
|
145
149
|
summary: z.ZodOptional<z.ZodString>;
|
|
150
|
+
method: z.ZodOptional<z.ZodString>;
|
|
146
151
|
}, z.core.$strip>>>;
|
|
147
152
|
}, z.core.$strip>>;
|
|
148
153
|
pricingCta: z.ZodOptional<z.ZodObject<{
|
|
@@ -63,6 +63,7 @@ export declare const LandingApiShowcaseItemSchema: z.ZodObject<{
|
|
|
63
63
|
icon: z.ZodOptional<z.ZodString>;
|
|
64
64
|
latency: z.ZodOptional<z.ZodString>;
|
|
65
65
|
summary: z.ZodOptional<z.ZodString>;
|
|
66
|
+
method: z.ZodOptional<z.ZodString>;
|
|
66
67
|
}, z.core.$strip>;
|
|
67
68
|
export declare const LandingApiShowcaseSchema: z.ZodOptional<z.ZodObject<{
|
|
68
69
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -71,12 +72,17 @@ export declare const LandingApiShowcaseSchema: z.ZodOptional<z.ZodObject<{
|
|
|
71
72
|
browseAllHref: z.ZodOptional<z.ZodString>;
|
|
72
73
|
browseAllLabel: z.ZodOptional<z.ZodString>;
|
|
73
74
|
autoFromOpenApi: z.ZodOptional<z.ZodBoolean>;
|
|
75
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
76
|
+
grid: "grid";
|
|
77
|
+
list: "list";
|
|
78
|
+
}>>;
|
|
74
79
|
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
75
80
|
name: z.ZodString;
|
|
76
81
|
path: z.ZodString;
|
|
77
82
|
icon: z.ZodOptional<z.ZodString>;
|
|
78
83
|
latency: z.ZodOptional<z.ZodString>;
|
|
79
84
|
summary: z.ZodOptional<z.ZodString>;
|
|
85
|
+
method: z.ZodOptional<z.ZodString>;
|
|
80
86
|
}, z.core.$strip>>>;
|
|
81
87
|
}, z.core.$strip>>;
|
|
82
88
|
export declare const LandingPricingCtaSchema: z.ZodOptional<z.ZodObject<{
|
|
@@ -174,12 +180,17 @@ export declare const LandingContentSchema: z.ZodOptional<z.ZodObject<{
|
|
|
174
180
|
browseAllHref: z.ZodOptional<z.ZodString>;
|
|
175
181
|
browseAllLabel: z.ZodOptional<z.ZodString>;
|
|
176
182
|
autoFromOpenApi: z.ZodOptional<z.ZodBoolean>;
|
|
183
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
184
|
+
grid: "grid";
|
|
185
|
+
list: "list";
|
|
186
|
+
}>>;
|
|
177
187
|
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
178
188
|
name: z.ZodString;
|
|
179
189
|
path: z.ZodString;
|
|
180
190
|
icon: z.ZodOptional<z.ZodString>;
|
|
181
191
|
latency: z.ZodOptional<z.ZodString>;
|
|
182
192
|
summary: z.ZodOptional<z.ZodString>;
|
|
193
|
+
method: z.ZodOptional<z.ZodString>;
|
|
183
194
|
}, z.core.$strip>>>;
|
|
184
195
|
}, z.core.$strip>>;
|
|
185
196
|
pricingCta: z.ZodOptional<z.ZodObject<{
|
|
@@ -303,12 +314,17 @@ export declare const LandingModuleSchema: z.ZodObject<{
|
|
|
303
314
|
browseAllHref: z.ZodOptional<z.ZodString>;
|
|
304
315
|
browseAllLabel: z.ZodOptional<z.ZodString>;
|
|
305
316
|
autoFromOpenApi: z.ZodOptional<z.ZodBoolean>;
|
|
317
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
318
|
+
grid: "grid";
|
|
319
|
+
list: "list";
|
|
320
|
+
}>>;
|
|
306
321
|
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
307
322
|
name: z.ZodString;
|
|
308
323
|
path: z.ZodString;
|
|
309
324
|
icon: z.ZodOptional<z.ZodString>;
|
|
310
325
|
latency: z.ZodOptional<z.ZodString>;
|
|
311
326
|
summary: z.ZodOptional<z.ZodString>;
|
|
327
|
+
method: z.ZodOptional<z.ZodString>;
|
|
312
328
|
}, z.core.$strip>>>;
|
|
313
329
|
}, z.core.$strip>>;
|
|
314
330
|
pricingCta: z.ZodOptional<z.ZodObject<{
|
|
@@ -553,12 +569,17 @@ export declare const ModulesSchema: z.ZodOptional<z.ZodObject<{
|
|
|
553
569
|
browseAllHref: z.ZodOptional<z.ZodString>;
|
|
554
570
|
browseAllLabel: z.ZodOptional<z.ZodString>;
|
|
555
571
|
autoFromOpenApi: z.ZodOptional<z.ZodBoolean>;
|
|
572
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
573
|
+
grid: "grid";
|
|
574
|
+
list: "list";
|
|
575
|
+
}>>;
|
|
556
576
|
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
557
577
|
name: z.ZodString;
|
|
558
578
|
path: z.ZodString;
|
|
559
579
|
icon: z.ZodOptional<z.ZodString>;
|
|
560
580
|
latency: z.ZodOptional<z.ZodString>;
|
|
561
581
|
summary: z.ZodOptional<z.ZodString>;
|
|
582
|
+
method: z.ZodOptional<z.ZodString>;
|
|
562
583
|
}, z.core.$strip>>>;
|
|
563
584
|
}, z.core.$strip>>;
|
|
564
585
|
pricingCta: z.ZodOptional<z.ZodObject<{
|
|
@@ -6933,12 +6933,17 @@ export declare const ZudokuConfig: z.ZodObject<{
|
|
|
6933
6933
|
browseAllHref: z.ZodOptional<z.ZodString>;
|
|
6934
6934
|
browseAllLabel: z.ZodOptional<z.ZodString>;
|
|
6935
6935
|
autoFromOpenApi: z.ZodOptional<z.ZodBoolean>;
|
|
6936
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
6937
|
+
grid: "grid";
|
|
6938
|
+
list: "list";
|
|
6939
|
+
}>>;
|
|
6936
6940
|
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
6937
6941
|
name: z.ZodString;
|
|
6938
6942
|
path: z.ZodString;
|
|
6939
6943
|
icon: z.ZodOptional<z.ZodString>;
|
|
6940
6944
|
latency: z.ZodOptional<z.ZodString>;
|
|
6941
6945
|
summary: z.ZodOptional<z.ZodString>;
|
|
6946
|
+
method: z.ZodOptional<z.ZodString>;
|
|
6942
6947
|
}, z.core.$strip>>>;
|
|
6943
6948
|
}, z.core.$strip>>;
|
|
6944
6949
|
pricingCta: z.ZodOptional<z.ZodObject<{
|
package/dist/flat-config.d.ts
CHANGED
|
@@ -575,12 +575,14 @@ export interface _Schema21 {
|
|
|
575
575
|
browseAllHref?: string
|
|
576
576
|
browseAllLabel?: string
|
|
577
577
|
autoFromOpenApi?: boolean
|
|
578
|
+
layout?: ("grid" | "list")
|
|
578
579
|
items?: {
|
|
579
580
|
name: string
|
|
580
581
|
path: string
|
|
581
582
|
icon?: string
|
|
582
583
|
latency?: string
|
|
583
584
|
summary?: string
|
|
585
|
+
method?: string
|
|
584
586
|
}[]
|
|
585
587
|
}
|
|
586
588
|
pricingCta?: {
|
package/package.json
CHANGED
|
@@ -3,44 +3,45 @@ import type { ZudokuConfig } from "../config/validators/ZudokuConfig.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* APIToGo brand theme for per-site overrides via apitogo.config.
|
|
5
5
|
* Keep in sync with defaultTheme.css (full palette including sidebar/chart tokens).
|
|
6
|
+
* Sourced from Sample-API-Website-UI (teal brand + navy/mist surfaces).
|
|
6
7
|
*/
|
|
7
8
|
export const apitogoBrandTheme: NonNullable<ZudokuConfig["theme"]> = {
|
|
8
9
|
fonts: {
|
|
9
|
-
sans: "
|
|
10
|
+
sans: "Inter",
|
|
10
11
|
mono: "JetBrains Mono",
|
|
11
12
|
},
|
|
12
13
|
light: {
|
|
13
|
-
background: "#
|
|
14
|
-
foreground: "#
|
|
15
|
-
card: "#
|
|
16
|
-
cardForeground: "#
|
|
17
|
-
primary: "#
|
|
14
|
+
background: "#ffffff",
|
|
15
|
+
foreground: "#252b3b",
|
|
16
|
+
card: "#ffffff",
|
|
17
|
+
cardForeground: "#252b3b",
|
|
18
|
+
primary: "#0d9488",
|
|
18
19
|
primaryForeground: "#ffffff",
|
|
19
|
-
secondary: "#
|
|
20
|
-
secondaryForeground: "#
|
|
21
|
-
muted: "#
|
|
22
|
-
mutedForeground: "
|
|
23
|
-
accent: "rgba(
|
|
24
|
-
accentForeground: "#
|
|
25
|
-
border: "#
|
|
26
|
-
input: "#
|
|
27
|
-
ring: "#
|
|
20
|
+
secondary: "#f4f6fb",
|
|
21
|
+
secondaryForeground: "#252b3b",
|
|
22
|
+
muted: "#e9edf5",
|
|
23
|
+
mutedForeground: "rgba(37, 43, 59, 0.55)",
|
|
24
|
+
accent: "rgba(13, 148, 136, 0.1)",
|
|
25
|
+
accentForeground: "#0d9488",
|
|
26
|
+
border: "#d9deea",
|
|
27
|
+
input: "#d9deea",
|
|
28
|
+
ring: "#0d9488",
|
|
28
29
|
},
|
|
29
30
|
dark: {
|
|
30
|
-
background: "#
|
|
31
|
-
foreground: "#
|
|
32
|
-
card: "#
|
|
33
|
-
cardForeground: "#
|
|
34
|
-
primary: "#
|
|
31
|
+
background: "#252b3b",
|
|
32
|
+
foreground: "#e9edf5",
|
|
33
|
+
card: "#2b3243",
|
|
34
|
+
cardForeground: "#e9edf5",
|
|
35
|
+
primary: "#0d9488",
|
|
35
36
|
primaryForeground: "#ffffff",
|
|
36
|
-
secondary: "#
|
|
37
|
-
secondaryForeground: "#
|
|
38
|
-
muted: "#
|
|
39
|
-
mutedForeground: "
|
|
40
|
-
accent: "rgba(
|
|
41
|
-
accentForeground: "#
|
|
42
|
-
border: "
|
|
43
|
-
input: "#
|
|
44
|
-
ring: "#
|
|
37
|
+
secondary: "#1e2330",
|
|
38
|
+
secondaryForeground: "#e9edf5",
|
|
39
|
+
muted: "#1e2330",
|
|
40
|
+
mutedForeground: "rgba(217, 222, 234, 0.58)",
|
|
41
|
+
accent: "rgba(13, 148, 136, 0.12)",
|
|
42
|
+
accentForeground: "#0d9488",
|
|
43
|
+
border: "rgba(217, 222, 234, 0.18)",
|
|
44
|
+
input: "#323a4f",
|
|
45
|
+
ring: "#0d9488",
|
|
45
46
|
},
|
|
46
47
|
};
|
package/src/app/defaultTheme.css
CHANGED
|
@@ -1,77 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Default APIToGo brand theme for scaffolded sites.
|
|
3
|
-
* Color tokens sourced from
|
|
3
|
+
* Color tokens sourced from Sample-API-Website-UI (teal brand + navy/mist surfaces).
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
:root {
|
|
7
7
|
--radius: 0.625rem;
|
|
8
|
-
--background: #
|
|
9
|
-
--foreground: #
|
|
8
|
+
--background: #ffffff;
|
|
9
|
+
--foreground: #252b3b;
|
|
10
10
|
--card: #ffffff;
|
|
11
|
-
--card-foreground: #
|
|
11
|
+
--card-foreground: #252b3b;
|
|
12
12
|
--popover: #ffffff;
|
|
13
|
-
--popover-foreground: #
|
|
14
|
-
--primary: #
|
|
13
|
+
--popover-foreground: #252b3b;
|
|
14
|
+
--primary: #0d9488;
|
|
15
15
|
--primary-foreground: #ffffff;
|
|
16
|
-
--secondary: #
|
|
17
|
-
--secondary-foreground: #
|
|
18
|
-
--muted: #
|
|
19
|
-
--muted-foreground:
|
|
20
|
-
--accent: rgba(
|
|
21
|
-
--accent-foreground: #
|
|
22
|
-
--destructive: #
|
|
16
|
+
--secondary: #f4f6fb;
|
|
17
|
+
--secondary-foreground: #252b3b;
|
|
18
|
+
--muted: #e9edf5;
|
|
19
|
+
--muted-foreground: rgba(37, 43, 59, 0.55);
|
|
20
|
+
--accent: rgba(13, 148, 136, 0.1);
|
|
21
|
+
--accent-foreground: #0d9488;
|
|
22
|
+
--destructive: #e02424;
|
|
23
23
|
--warning: oklch(0.84 0.16 84);
|
|
24
24
|
--warning-foreground: oklch(0.47 0.14 46.2);
|
|
25
|
-
--border: #
|
|
26
|
-
--input: #
|
|
27
|
-
--ring: #
|
|
28
|
-
--chart-1: #
|
|
29
|
-
--chart-2: #
|
|
30
|
-
--chart-3: #
|
|
31
|
-
--chart-4: #
|
|
32
|
-
--chart-5: #
|
|
25
|
+
--border: #d9deea;
|
|
26
|
+
--input: #d9deea;
|
|
27
|
+
--ring: #0d9488;
|
|
28
|
+
--chart-1: #0d9488;
|
|
29
|
+
--chart-2: #0e9f6e;
|
|
30
|
+
--chart-3: #3f83f8;
|
|
31
|
+
--chart-4: #8b95af;
|
|
32
|
+
--chart-5: #454e66;
|
|
33
33
|
--sidebar: #ffffff;
|
|
34
|
-
--sidebar-foreground: #
|
|
35
|
-
--sidebar-primary: #
|
|
34
|
+
--sidebar-foreground: #252b3b;
|
|
35
|
+
--sidebar-primary: #0d9488;
|
|
36
36
|
--sidebar-primary-foreground: #ffffff;
|
|
37
|
-
--sidebar-accent: #
|
|
38
|
-
--sidebar-accent-foreground: #
|
|
39
|
-
--sidebar-border: #
|
|
40
|
-
--sidebar-ring: #
|
|
37
|
+
--sidebar-accent: #f4f6fb;
|
|
38
|
+
--sidebar-accent-foreground: #252b3b;
|
|
39
|
+
--sidebar-border: #d9deea;
|
|
40
|
+
--sidebar-ring: #0d9488;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
.dark {
|
|
44
|
-
--background: #
|
|
45
|
-
--foreground: #
|
|
46
|
-
--card: #
|
|
47
|
-
--card-foreground: #
|
|
48
|
-
--popover: #
|
|
49
|
-
--popover-foreground: #
|
|
50
|
-
--primary: #
|
|
44
|
+
--background: #252b3b;
|
|
45
|
+
--foreground: #e9edf5;
|
|
46
|
+
--card: #2b3243;
|
|
47
|
+
--card-foreground: #e9edf5;
|
|
48
|
+
--popover: #2b3243;
|
|
49
|
+
--popover-foreground: #e9edf5;
|
|
50
|
+
--primary: #0d9488;
|
|
51
51
|
--primary-foreground: #ffffff;
|
|
52
|
-
--secondary: #
|
|
53
|
-
--secondary-foreground: #
|
|
54
|
-
--muted: #
|
|
55
|
-
--muted-foreground:
|
|
56
|
-
--accent: rgba(
|
|
57
|
-
--accent-foreground: #
|
|
58
|
-
--destructive: #
|
|
52
|
+
--secondary: #1e2330;
|
|
53
|
+
--secondary-foreground: #e9edf5;
|
|
54
|
+
--muted: #1e2330;
|
|
55
|
+
--muted-foreground: rgba(217, 222, 234, 0.58);
|
|
56
|
+
--accent: rgba(13, 148, 136, 0.12);
|
|
57
|
+
--accent-foreground: #0d9488;
|
|
58
|
+
--destructive: #e02424;
|
|
59
59
|
--warning: oklch(0.41 0.11 46);
|
|
60
60
|
--warning-foreground: oklch(0.99 0.02 95);
|
|
61
|
-
--border:
|
|
62
|
-
--input: #
|
|
63
|
-
--ring: #
|
|
64
|
-
--chart-1: #
|
|
65
|
-
--chart-2: #
|
|
66
|
-
--chart-3: #
|
|
67
|
-
--chart-4: #
|
|
68
|
-
--chart-5: #
|
|
69
|
-
--sidebar: #
|
|
70
|
-
--sidebar-foreground: #
|
|
71
|
-
--sidebar-primary: #
|
|
61
|
+
--border: rgba(217, 222, 234, 0.18);
|
|
62
|
+
--input: #323a4f;
|
|
63
|
+
--ring: #0d9488;
|
|
64
|
+
--chart-1: #0d9488;
|
|
65
|
+
--chart-2: #0e9f6e;
|
|
66
|
+
--chart-3: #3f83f8;
|
|
67
|
+
--chart-4: #8b95af;
|
|
68
|
+
--chart-5: #b9c1d6;
|
|
69
|
+
--sidebar: #1e2330;
|
|
70
|
+
--sidebar-foreground: #e9edf5;
|
|
71
|
+
--sidebar-primary: #0d9488;
|
|
72
72
|
--sidebar-primary-foreground: #ffffff;
|
|
73
|
-
--sidebar-accent: #
|
|
74
|
-
--sidebar-accent-foreground: #
|
|
75
|
-
--sidebar-border:
|
|
76
|
-
--sidebar-ring: #
|
|
73
|
+
--sidebar-accent: #252b3b;
|
|
74
|
+
--sidebar-accent-foreground: #e9edf5;
|
|
75
|
+
--sidebar-border: rgba(217, 222, 234, 0.18);
|
|
76
|
+
--sidebar-ring: #0d9488;
|
|
77
77
|
}
|
|
@@ -94,6 +94,7 @@ export const LandingApiShowcaseItemSchema = z.object({
|
|
|
94
94
|
icon: z.string().optional(),
|
|
95
95
|
latency: z.string().optional(),
|
|
96
96
|
summary: z.string().optional(),
|
|
97
|
+
method: z.string().optional(),
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
export const LandingApiShowcaseSchema = z
|
|
@@ -104,6 +105,7 @@ export const LandingApiShowcaseSchema = z
|
|
|
104
105
|
browseAllHref: z.string().optional(),
|
|
105
106
|
browseAllLabel: z.string().optional(),
|
|
106
107
|
autoFromOpenApi: z.boolean().optional(),
|
|
108
|
+
layout: z.enum(["grid", "list"]).optional(),
|
|
107
109
|
items: z.array(LandingApiShowcaseItemSchema).optional(),
|
|
108
110
|
})
|
|
109
111
|
.optional();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Spinner } from "@lukoweb/apitogo/components";
|
|
2
|
+
import { Badge } from "@lukoweb/apitogo/ui/Badge.js";
|
|
2
3
|
import { Button } from "@lukoweb/apitogo/ui/Button.js";
|
|
3
4
|
import {
|
|
4
5
|
Card,
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
} from "@lukoweb/apitogo/ui/Card.js";
|
|
10
11
|
import { Input } from "@lukoweb/apitogo/ui/Input.js";
|
|
11
12
|
import { Label } from "@lukoweb/apitogo/ui/Label.js";
|
|
12
|
-
import { useCallback, useEffect, useState } from "react";
|
|
13
|
+
import { useCallback, useEffect, useState, type ReactNode } from "react";
|
|
13
14
|
import { Link, useSearchParams } from "react-router";
|
|
14
15
|
import type { DevPortalAuthConfigResponse } from "./dev-portal-constants.js";
|
|
15
16
|
import {
|
|
@@ -31,20 +32,31 @@ import {
|
|
|
31
32
|
type AuthPageShellProps = {
|
|
32
33
|
title: string;
|
|
33
34
|
description?: string;
|
|
34
|
-
|
|
35
|
+
badge?: ReactNode;
|
|
36
|
+
children: ReactNode;
|
|
35
37
|
};
|
|
36
38
|
|
|
37
|
-
function AuthPageShell({
|
|
39
|
+
function AuthPageShell({
|
|
40
|
+
title,
|
|
41
|
+
description,
|
|
42
|
+
badge,
|
|
43
|
+
children,
|
|
44
|
+
}: AuthPageShellProps) {
|
|
38
45
|
return (
|
|
39
|
-
<div className="flex items-center justify-center
|
|
40
|
-
<Card className="max-w-md
|
|
41
|
-
<CardHeader>
|
|
42
|
-
<
|
|
46
|
+
<div className="flex items-center justify-center bg-muted px-5 py-10">
|
|
47
|
+
<Card className="w-full max-w-md gap-0 bg-card py-8">
|
|
48
|
+
<CardHeader className="gap-0 px-8 pb-0">
|
|
49
|
+
{badge ? <div className="mb-3.5">{badge}</div> : null}
|
|
50
|
+
<CardTitle className="text-2xl font-bold tracking-tight">
|
|
51
|
+
{title}
|
|
52
|
+
</CardTitle>
|
|
43
53
|
{description ? (
|
|
44
|
-
<CardDescription>
|
|
54
|
+
<CardDescription className="mt-1 mb-[22px] text-sm">
|
|
55
|
+
{description}
|
|
56
|
+
</CardDescription>
|
|
45
57
|
) : null}
|
|
46
58
|
</CardHeader>
|
|
47
|
-
<CardContent className="space-y-4">{children}</CardContent>
|
|
59
|
+
<CardContent className="space-y-4 px-8">{children}</CardContent>
|
|
48
60
|
</Card>
|
|
49
61
|
</div>
|
|
50
62
|
);
|
|
@@ -349,7 +361,7 @@ export function DevPortalRegisterPage({
|
|
|
349
361
|
if (!baseUrl) {
|
|
350
362
|
return (
|
|
351
363
|
<AuthPageShell
|
|
352
|
-
title="Create account"
|
|
364
|
+
title="Create your account"
|
|
353
365
|
description="Dev portal API is not configured."
|
|
354
366
|
>
|
|
355
367
|
<p className="text-sm text-muted-foreground">
|
|
@@ -361,16 +373,17 @@ export function DevPortalRegisterPage({
|
|
|
361
373
|
|
|
362
374
|
return (
|
|
363
375
|
<AuthPageShell
|
|
364
|
-
title="Create account"
|
|
365
|
-
description="Register for API access."
|
|
376
|
+
title="Create your account"
|
|
366
377
|
>
|
|
367
|
-
<form className="
|
|
378
|
+
<form className="flex flex-col gap-4" onSubmit={onSubmit}>
|
|
368
379
|
<div className="space-y-2">
|
|
369
|
-
<Label htmlFor="displayName">
|
|
380
|
+
<Label htmlFor="displayName">Full name</Label>
|
|
370
381
|
<Input
|
|
371
382
|
id="displayName"
|
|
383
|
+
placeholder="Priya Shah"
|
|
372
384
|
value={displayName}
|
|
373
385
|
onChange={(event) => setDisplayName(event.target.value)}
|
|
386
|
+
className="h-10"
|
|
374
387
|
/>
|
|
375
388
|
</div>
|
|
376
389
|
<div className="space-y-2">
|
|
@@ -379,9 +392,11 @@ export function DevPortalRegisterPage({
|
|
|
379
392
|
id="email"
|
|
380
393
|
type="email"
|
|
381
394
|
autoComplete="email"
|
|
395
|
+
placeholder="you@company.com"
|
|
382
396
|
value={email}
|
|
383
397
|
onChange={(event) => setEmail(event.target.value)}
|
|
384
398
|
required
|
|
399
|
+
className="h-10"
|
|
385
400
|
/>
|
|
386
401
|
</div>
|
|
387
402
|
<div className="space-y-2">
|
|
@@ -390,24 +405,36 @@ export function DevPortalRegisterPage({
|
|
|
390
405
|
id="password"
|
|
391
406
|
type="password"
|
|
392
407
|
autoComplete="new-password"
|
|
408
|
+
placeholder="••••••••"
|
|
393
409
|
value={password}
|
|
394
410
|
onChange={(event) => setPassword(event.target.value)}
|
|
395
411
|
required
|
|
412
|
+
className="h-10"
|
|
396
413
|
/>
|
|
414
|
+
<p className="text-xs text-muted-foreground">At least 8 characters</p>
|
|
397
415
|
</div>
|
|
398
416
|
<AuthError message={error} />
|
|
399
417
|
{message ? (
|
|
400
418
|
<p className="text-sm text-muted-foreground">{message}</p>
|
|
401
419
|
) : null}
|
|
402
|
-
<Button
|
|
420
|
+
<Button
|
|
421
|
+
type="submit"
|
|
422
|
+
size="lg"
|
|
423
|
+
className="h-12 w-full text-base"
|
|
424
|
+
disabled={pending}
|
|
425
|
+
>
|
|
403
426
|
{pending ? <Spinner /> : "Create account"}
|
|
404
427
|
</Button>
|
|
405
|
-
<p className="text-
|
|
428
|
+
<p className="text-xs leading-relaxed text-muted-foreground">
|
|
429
|
+
By creating an account you agree to our Terms and Privacy Policy.
|
|
430
|
+
</p>
|
|
431
|
+
<p className="text-sm text-muted-foreground">
|
|
432
|
+
Already have an account?{" "}
|
|
406
433
|
<Link
|
|
407
|
-
className="text-primary hover:underline"
|
|
434
|
+
className="font-semibold text-primary no-underline hover:underline"
|
|
408
435
|
to={`/login?redirect=${encodeURIComponent(returnUrl)}`}
|
|
409
436
|
>
|
|
410
|
-
|
|
437
|
+
Log in
|
|
411
438
|
</Link>
|
|
412
439
|
</p>
|
|
413
440
|
</form>
|
|
@@ -148,6 +148,7 @@ export const Header = memo(function HeaderInner() {
|
|
|
148
148
|
const authPlacement = header?.placements?.auth ?? "end";
|
|
149
149
|
const authPosition =
|
|
150
150
|
authPlacement === "navigation" ? navPosition : authPlacement;
|
|
151
|
+
const hasMarketingHeaderNav = (header?.navigation?.length ?? 0) > 0;
|
|
151
152
|
|
|
152
153
|
const logoLightSrc = site?.logo
|
|
153
154
|
? /https?:\/\//.test(site.logo.src.light)
|
|
@@ -206,11 +207,13 @@ export const Header = memo(function HeaderInner() {
|
|
|
206
207
|
</div>
|
|
207
208
|
</Link>
|
|
208
209
|
<Slot.Target name="head-navigation-start" />
|
|
209
|
-
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
{!hasMarketingHeaderNav ? (
|
|
211
|
+
<div className="hidden lg:flex min-w-0 items-center ml-2">
|
|
212
|
+
<Slot.Target name="top-navigation-before" />
|
|
213
|
+
<TopNavigation />
|
|
214
|
+
<Slot.Target name="top-navigation-after" />
|
|
215
|
+
</div>
|
|
216
|
+
) : null}
|
|
214
217
|
{searchPosition === "start" && (
|
|
215
218
|
<Search className="hidden lg:flex" />
|
|
216
219
|
)}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Button } from "@lukoweb/apitogo/ui/Button.js";
|
|
2
|
-
import { ArrowRightIcon } from "lucide-react";
|
|
3
2
|
import { useAuth } from "../authentication/hook.js";
|
|
4
3
|
import { cn } from "../util/cn.js";
|
|
5
4
|
|
|
@@ -15,19 +14,18 @@ export const HeaderAuthActions = ({ className }: { className?: string }) => {
|
|
|
15
14
|
<div className={cn("hidden lg:flex items-center gap-2.5", className)}>
|
|
16
15
|
<Button
|
|
17
16
|
variant="ghost"
|
|
18
|
-
size="
|
|
17
|
+
size="sm"
|
|
19
18
|
className="font-semibold text-foreground hover:text-primary px-3"
|
|
20
19
|
onClick={() => void auth.login()}
|
|
21
20
|
>
|
|
22
|
-
|
|
21
|
+
Log in
|
|
23
22
|
</Button>
|
|
24
23
|
<Button
|
|
25
|
-
size="
|
|
26
|
-
className="
|
|
24
|
+
size="sm"
|
|
25
|
+
className="rounded-[9px] px-4 font-semibold shadow-[0_4px_14px_-4px] shadow-primary/30"
|
|
27
26
|
onClick={() => void auth.signup()}
|
|
28
27
|
>
|
|
29
|
-
|
|
30
|
-
<ArrowRightIcon size={15} strokeWidth={2} />
|
|
28
|
+
Get started
|
|
31
29
|
</Button>
|
|
32
30
|
</div>
|
|
33
31
|
);
|
|
@@ -4,7 +4,10 @@ import { Outlet } from "react-router";
|
|
|
4
4
|
import { cn } from "../util/cn.js";
|
|
5
5
|
import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
|
|
6
6
|
import { useScrollToTop } from "../util/useScrollToTop.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
useCurrentNavigation,
|
|
9
|
+
useZudoku,
|
|
10
|
+
} from "./context/ZudokuContext.js";
|
|
8
11
|
import { Footer } from "./Footer.js";
|
|
9
12
|
import { Header } from "./Header.js";
|
|
10
13
|
import { Main } from "./Main.js";
|
|
@@ -19,6 +22,8 @@ const LoadingFallback = () => (
|
|
|
19
22
|
|
|
20
23
|
export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
21
24
|
const { authentication } = useZudoku();
|
|
25
|
+
const { navigation } = useCurrentNavigation();
|
|
26
|
+
const hasSideNav = navigation.length > 0;
|
|
22
27
|
|
|
23
28
|
useScrollToAnchor();
|
|
24
29
|
useScrollToTop();
|
|
@@ -38,9 +43,13 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
38
43
|
|
|
39
44
|
<div
|
|
40
45
|
className={cn(
|
|
41
|
-
"grid
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
"grid w-full",
|
|
47
|
+
// Docs shell: cap width + reserve side-nav column.
|
|
48
|
+
// Marketing pages (no side nav): full-bleed so section backgrounds
|
|
49
|
+
// and compare/FAQ bands can span the viewport.
|
|
50
|
+
hasSideNav
|
|
51
|
+
? "max-w-screen-2xl lg:mx-auto grid-rows-[0_min-content_1fr] lg:grid-rows-[min-content_1fr] grid-cols-1 lg:grid-cols-[var(--side-nav-width)_1fr]"
|
|
52
|
+
: "grid-cols-1 grid-rows-1",
|
|
44
53
|
)}
|
|
45
54
|
>
|
|
46
55
|
<Suspense fallback={<LoadingFallback />}>
|
|
@@ -37,8 +37,9 @@ export const Main = ({ children }: PropsWithChildren) => {
|
|
|
37
37
|
<main
|
|
38
38
|
data-pagefind-body
|
|
39
39
|
className={cn(
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// Docs: gutter so content clears the side nav.
|
|
41
|
+
// Marketing: no shell padding — pages own section px / full-bleed bands.
|
|
42
|
+
hasNavigation ? "px-4 lg:pe-8 lg:px-8" : "col-span-full px-0",
|
|
42
43
|
isNavigating && "animate-pulse",
|
|
43
44
|
)}
|
|
44
45
|
>
|
|
@@ -3,7 +3,6 @@ import { Separator } from "@lukoweb/apitogo/ui/Separator.js";
|
|
|
3
3
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
4
4
|
import { deepEqual } from "fast-equals";
|
|
5
5
|
import {
|
|
6
|
-
ArrowRightIcon,
|
|
7
6
|
ChevronDownIcon,
|
|
8
7
|
LogOutIcon,
|
|
9
8
|
MenuIcon,
|
|
@@ -258,17 +257,15 @@ export const MobileTopNavigation = () => {
|
|
|
258
257
|
void login();
|
|
259
258
|
}}
|
|
260
259
|
>
|
|
261
|
-
|
|
260
|
+
Log in
|
|
262
261
|
</Button>
|
|
263
262
|
<Button
|
|
264
|
-
className="gap-1.5"
|
|
265
263
|
onClick={() => {
|
|
266
264
|
setDrawerOpen(false);
|
|
267
265
|
void signup();
|
|
268
266
|
}}
|
|
269
267
|
>
|
|
270
|
-
|
|
271
|
-
<ArrowRightIcon size={15} strokeWidth={2} />
|
|
268
|
+
Get started
|
|
272
269
|
</Button>
|
|
273
270
|
</div>
|
|
274
271
|
))}
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import { MoonIcon, SunIcon } from "lucide-react";
|
|
1
|
+
import { MoonIcon, MonitorIcon, SunIcon } from "lucide-react";
|
|
2
2
|
import { useTheme } from "next-themes";
|
|
3
3
|
import { focusRing } from "../ui/util.js";
|
|
4
4
|
import { cn } from "../util/cn.js";
|
|
5
5
|
import { useIsClient } from "./ClientOnly.js";
|
|
6
6
|
|
|
7
|
+
const COMPACT_OPTIONS = [
|
|
8
|
+
{ value: "light", label: "Light", Icon: SunIcon },
|
|
9
|
+
{ value: "dark", label: "Dark", Icon: MoonIcon },
|
|
10
|
+
{ value: "system", label: "System", Icon: MonitorIcon },
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
7
13
|
export const ThemeSwitch = ({
|
|
8
14
|
variant = "default",
|
|
9
15
|
}: {
|
|
10
16
|
variant?: "default" | "compact";
|
|
11
17
|
}) => {
|
|
12
|
-
const { resolvedTheme, setTheme } = useTheme();
|
|
18
|
+
const { theme, resolvedTheme, setTheme } = useTheme();
|
|
13
19
|
const isClient = useIsClient();
|
|
14
20
|
|
|
15
|
-
const
|
|
21
|
+
const activeTheme = isClient ? resolvedTheme : undefined;
|
|
22
|
+
const selectedMode = isClient ? (theme ?? "system") : "system";
|
|
16
23
|
|
|
17
24
|
const handleThemeToggle = () => {
|
|
18
25
|
setTheme(resolvedTheme === "dark" ? "light" : "dark");
|
|
@@ -20,27 +27,36 @@ export const ThemeSwitch = ({
|
|
|
20
27
|
|
|
21
28
|
if (variant === "compact") {
|
|
22
29
|
return (
|
|
23
|
-
<
|
|
24
|
-
|
|
30
|
+
<div
|
|
31
|
+
role="group"
|
|
32
|
+
aria-label="Theme"
|
|
25
33
|
className={cn(
|
|
26
|
-
"
|
|
34
|
+
"inline-flex h-8 items-center gap-0.5 rounded-[9px] border border-border bg-muted/40 p-0.5",
|
|
27
35
|
focusRing,
|
|
28
36
|
)}
|
|
29
|
-
onClick={handleThemeToggle}
|
|
30
|
-
aria-label={
|
|
31
|
-
!isClient
|
|
32
|
-
? "Toggle theme"
|
|
33
|
-
: theme === "dark"
|
|
34
|
-
? "Switch to light mode"
|
|
35
|
-
: "Switch to dark mode"
|
|
36
|
-
}
|
|
37
37
|
>
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
{COMPACT_OPTIONS.map(({ value, label, Icon }) => {
|
|
39
|
+
const selected = selectedMode === value;
|
|
40
|
+
return (
|
|
41
|
+
<button
|
|
42
|
+
key={value}
|
|
43
|
+
type="button"
|
|
44
|
+
className={cn(
|
|
45
|
+
"grid size-7 place-items-center rounded-[7px] text-muted-foreground transition-colors",
|
|
46
|
+
selected
|
|
47
|
+
? "bg-card text-foreground shadow-xs"
|
|
48
|
+
: "hover:text-foreground",
|
|
49
|
+
)}
|
|
50
|
+
onClick={() => setTheme(value)}
|
|
51
|
+
aria-label={label}
|
|
52
|
+
aria-pressed={selected}
|
|
53
|
+
title={label}
|
|
54
|
+
>
|
|
55
|
+
<Icon size={14} strokeWidth={1.8} />
|
|
56
|
+
</button>
|
|
57
|
+
);
|
|
58
|
+
})}
|
|
59
|
+
</div>
|
|
44
60
|
);
|
|
45
61
|
}
|
|
46
62
|
|
|
@@ -52,7 +68,7 @@ export const ThemeSwitch = ({
|
|
|
52
68
|
aria-label={
|
|
53
69
|
!isClient
|
|
54
70
|
? "Toggle theme"
|
|
55
|
-
:
|
|
71
|
+
: activeTheme === "dark"
|
|
56
72
|
? "Switch to light mode"
|
|
57
73
|
: "Switch to dark mode"
|
|
58
74
|
}
|
|
@@ -60,8 +76,8 @@ export const ThemeSwitch = ({
|
|
|
60
76
|
<div
|
|
61
77
|
className={cn(
|
|
62
78
|
"rounded-full p-0.5 border border-transparent [&>svg>circle]:transition-colors [&>svg>path]:transition-transform transition-all [&>svg>path]:duration-200 [&>svg>circle]:duration-500 [&>svg>circle]:fill-transparent",
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
activeTheme === "light" && "border-border bg-muted",
|
|
80
|
+
activeTheme === "dark" &&
|
|
65
81
|
"group-hover:[&>svg>path]:scale-110 group-hover:[&>svg>path]:-translate-x-px group-hover:[&>svg>path]:-translate-y-px group-hover:rotate-15",
|
|
66
82
|
)}
|
|
67
83
|
>
|
|
@@ -70,9 +86,9 @@ export const ThemeSwitch = ({
|
|
|
70
86
|
<div
|
|
71
87
|
className={cn(
|
|
72
88
|
"rounded-full p-0.5 border border-transparent transition-transform transform-gpu duration-500",
|
|
73
|
-
|
|
89
|
+
activeTheme === "dark" &&
|
|
74
90
|
"border-border bg-muted [&>svg>path]:fill-white [&>svg>path]:stroke-transparent",
|
|
75
|
-
|
|
91
|
+
activeTheme === "light" &&
|
|
76
92
|
"group-hover:rotate-[-10deg] [&>svg>path]:stroke-currentColor",
|
|
77
93
|
)}
|
|
78
94
|
>
|