create-vexcms 0.1.0-alpha.2 → 0.1.0-alpha.21
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/README.md +48 -14
- package/dist/index.js +1714 -1504
- package/package.json +2 -2
- package/templates/base-nextjs/README.md +18 -5
- package/templates/base-nextjs/convex/auth/index.ts +2 -2
- package/templates/base-nextjs/convex/auth/options.ts +27 -26
- package/templates/base-nextjs/convex/auth/schema.ts +51 -0
- package/templates/base-nextjs/convex/auth/sessions.ts +3 -3
- package/templates/base-nextjs/convex/tsconfig.json +2 -2
- package/templates/base-nextjs/convex/vex/firstUser.ts +2 -2
- package/templates/base-nextjs/convex/vex/globals.ts +3 -2
- package/templates/base-nextjs/convex/vex/media.ts +3 -2
- package/templates/base-nextjs/convex/vex.schema.ts +1 -0
- package/templates/base-nextjs/convex/vex.ts +17 -4
- package/templates/base-nextjs/next.config.ts +52 -6
- package/templates/base-nextjs/package.json +18 -7
- package/templates/base-nextjs/public/favicons/favicon.ico +0 -0
- package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +27 -15
- package/templates/base-nextjs/src/app/(vexcms)/admin/[[...path]]/page.tsx +2 -2
- package/templates/base-nextjs/src/app/(vexcms)/admin/clientProviders.tsx +9 -14
- package/templates/base-nextjs/src/app/(vexcms)/admin/layout.tsx +8 -6
- package/templates/base-nextjs/src/app/api/vex/revalidate/route.ts +19 -0
- package/templates/base-nextjs/src/app/globals.css +10 -9
- package/templates/base-nextjs/src/app/layout.tsx +6 -0
- package/templates/base-nextjs/src/auth/access.ts +14 -2
- package/templates/base-nextjs/src/auth/serverUtils.ts +20 -6
- package/templates/base-nextjs/src/components/WelcomePage.tsx +54 -43
- package/templates/base-nextjs/src/components/component-example.tsx +1 -1
- package/templates/base-nextjs/src/components/providers/server.tsx +16 -8
- package/templates/base-nextjs/src/vex.config.server.ts +30 -0
- package/templates/base-nextjs/src/vex.config.ts +14 -11
- package/templates/base-nextjs/src/vex.types.ts +2 -1
- package/templates/base-nextjs/src/vexcms/collections/images.ts +1 -1
- package/templates/base-nextjs/src/vexcms/collections/users.ts +3 -1
- package/templates/marketing-site/convex/pages.ts +20 -1
- package/templates/marketing-site/convex/schema.ts +4 -4
- package/templates/marketing-site/convex/seed.ts +515 -70
- package/templates/marketing-site/convex/siteSettings.ts +1 -1
- package/templates/marketing-site/convex/theme.ts +3 -3
- package/templates/marketing-site/convex/vex.schema.ts +75 -38
- package/templates/marketing-site/public/favicon.svg +14 -0
- package/templates/marketing-site/public/favicons/apple-touch-icon.png +0 -0
- package/templates/marketing-site/public/favicons/favicon.ico +0 -0
- package/templates/marketing-site/src/app/(frontend)/(site)/PageContent.tsx +64 -0
- package/templates/marketing-site/src/app/(frontend)/(site)/SiteLivePreviewProvider.tsx +31 -0
- package/templates/marketing-site/src/app/(frontend)/(site)/[slug]/page.tsx +63 -0
- package/templates/marketing-site/src/app/(frontend)/(site)/layout.tsx +50 -0
- package/templates/marketing-site/src/app/(frontend)/(site)/page.tsx +33 -0
- package/templates/marketing-site/src/app/(frontend)/layout.tsx +10 -24
- package/templates/marketing-site/src/app/(vexcms)/admin/layout.tsx +32 -0
- package/templates/marketing-site/src/app/globals.css +413 -0
- package/templates/marketing-site/src/app/layout.tsx +85 -0
- package/templates/marketing-site/src/app/robots.ts +13 -0
- package/templates/marketing-site/src/app/sitemap.ts +18 -0
- package/templates/marketing-site/src/components/BrandMark.tsx +27 -0
- package/templates/marketing-site/src/components/CodeHighlightContext.tsx +76 -0
- package/templates/marketing-site/src/components/CodePane.tsx +98 -0
- package/templates/marketing-site/src/components/Container.tsx +25 -0
- package/templates/marketing-site/src/components/CopyButton.tsx +80 -0
- package/templates/marketing-site/src/components/FirstAdminBootstrap.tsx +54 -0
- package/templates/marketing-site/src/components/InstallCommand.tsx +29 -0
- package/templates/marketing-site/src/components/MediaImage.tsx +59 -0
- package/templates/marketing-site/src/components/SectionHeader.tsx +51 -0
- package/templates/marketing-site/src/components/SiteFooter.tsx +8 -6
- package/templates/marketing-site/src/components/SiteHeader.tsx +8 -6
- package/templates/marketing-site/src/components/ThemeLive.tsx +1 -2
- package/templates/marketing-site/src/components/ThemeStyle.tsx +3 -3
- package/templates/marketing-site/src/components/motion-primitives/animated-group.tsx +44 -120
- package/templates/marketing-site/src/components/motion-primitives/reveal.module.css +32 -0
- package/templates/marketing-site/src/components/motion-primitives/text-effect.tsx +47 -266
- package/templates/marketing-site/src/lib/codeHighlightKey.ts +27 -0
- package/templates/marketing-site/src/lib/highlight.ts +134 -0
- package/templates/marketing-site/src/lib/highlightClient.ts +37 -0
- package/templates/marketing-site/src/lib/metadata.ts +61 -33
- package/templates/marketing-site/src/lib/resolvePagePath.ts +8 -0
- package/templates/marketing-site/src/lib/vex.ts +18 -0
- package/templates/marketing-site/src/proxy.ts +179 -0
- package/templates/marketing-site/src/vex.config.server.ts +27 -0
- package/templates/marketing-site/src/vex.config.ts +27 -9
- package/templates/marketing-site/src/vex.types.ts +102 -76
- package/templates/marketing-site/src/vexcms/api.ts +2 -2
- package/templates/marketing-site/src/vexcms/blocks/CTA/config.ts +3 -2
- package/templates/marketing-site/src/vexcms/blocks/CTA/index.tsx +40 -24
- package/templates/marketing-site/src/vexcms/blocks/CodeShowcase/config.ts +62 -0
- package/templates/marketing-site/src/vexcms/blocks/CodeShowcase/index.tsx +106 -0
- package/templates/marketing-site/src/vexcms/blocks/FAQ/config.ts +4 -3
- package/templates/marketing-site/src/vexcms/blocks/FAQ/index.tsx +63 -50
- package/templates/marketing-site/src/vexcms/blocks/Features/config.ts +1 -0
- package/templates/marketing-site/src/vexcms/blocks/Features/index.tsx +43 -58
- package/templates/marketing-site/src/vexcms/blocks/Footer/config.ts +6 -3
- package/templates/marketing-site/src/vexcms/blocks/Footer/index.tsx +90 -53
- package/templates/marketing-site/src/vexcms/blocks/Header/config.ts +10 -3
- package/templates/marketing-site/src/vexcms/blocks/Header/index.tsx +205 -138
- package/templates/marketing-site/src/vexcms/blocks/Hero/config.ts +27 -12
- package/templates/marketing-site/src/vexcms/blocks/Hero/index.tsx +174 -119
- package/templates/marketing-site/src/vexcms/blocks/HowItWorks/config.ts +2 -1
- package/templates/marketing-site/src/vexcms/blocks/HowItWorks/index.tsx +59 -51
- package/templates/marketing-site/src/vexcms/blocks/Roadmap/config.ts +26 -5
- package/templates/marketing-site/src/vexcms/blocks/Roadmap/index.tsx +256 -73
- package/templates/marketing-site/src/vexcms/blocks/Split/config.ts +62 -0
- package/templates/marketing-site/src/vexcms/blocks/Split/index.tsx +186 -0
- package/templates/marketing-site/src/vexcms/blocks/Stats/config.ts +35 -0
- package/templates/marketing-site/src/vexcms/blocks/Stats/index.tsx +77 -0
- package/templates/marketing-site/src/vexcms/blocks/config.ts +27 -11
- package/templates/marketing-site/src/vexcms/blocks/constants.ts +3 -0
- package/templates/marketing-site/src/vexcms/blocks/index.ts +9 -0
- package/templates/marketing-site/src/vexcms/collections/footers.ts +1 -0
- package/templates/marketing-site/src/vexcms/collections/headers.ts +1 -0
- package/templates/marketing-site/src/vexcms/collections/pages.ts +23 -1
- package/templates/marketing-site/src/vexcms/collections/themeColors.ts +14 -14
- package/templates/marketing-site/src/vexcms/collections/users.ts +20 -0
- package/dist/index.mjs +0 -30976
- package/templates/marketing-site/src/app/(frontend)/PageContent.tsx +0 -53
- package/templates/marketing-site/src/app/(frontend)/[slug]/page.tsx +0 -25
- package/templates/marketing-site/src/app/(frontend)/page.tsx +0 -21
|
@@ -2,172 +2,239 @@
|
|
|
2
2
|
|
|
3
3
|
import type { BlockComponentProps } from "@vexcms/react"
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
buttonVariants,
|
|
7
|
+
cn,
|
|
8
|
+
Separator,
|
|
9
|
+
Sheet,
|
|
10
|
+
SheetContent,
|
|
11
|
+
SheetTitle,
|
|
12
|
+
SheetTrigger,
|
|
13
|
+
useCanAccessAdminPanel,
|
|
14
|
+
} from "@vexcms/react"
|
|
15
|
+
import { Menu, Settings } from "lucide-react"
|
|
6
16
|
import Link from "next/link"
|
|
17
|
+
import { usePathname } from "next/navigation"
|
|
7
18
|
import { useEffect, useState } from "react"
|
|
8
19
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
20
|
+
import type { HeaderBlock } from "~/vex.types"
|
|
21
|
+
|
|
22
|
+
import { BrandMark } from "~/components/BrandMark"
|
|
23
|
+
import { Container } from "~/components/Container"
|
|
24
|
+
import { MediaImage } from "~/components/MediaImage"
|
|
11
25
|
|
|
12
26
|
export { headerBlock } from "./config"
|
|
13
27
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const
|
|
28
|
+
/** Past this many nav items the bar would crowd at `md`, so the Sheet holds
|
|
29
|
+
* on until `lg`. The bar never wraps to two lines and never shrinks its type. */
|
|
30
|
+
const NAV_CROWD_THRESHOLD = 7
|
|
31
|
+
|
|
32
|
+
/** Scroll distance at which the bar takes its background and rule. */
|
|
33
|
+
const SCROLL_THRESHOLD = 8
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Site header — a rule that appears when you scroll.
|
|
37
|
+
*
|
|
38
|
+
* Transparent over the hero at rest; past 8px it takes
|
|
39
|
+
* `bg-background/70 backdrop-blur-md` and a bottom border and loses 8px of
|
|
40
|
+
* height. The scroll listener is passive and rAF-throttled, and is the only
|
|
41
|
+
* scroll listener on the page.
|
|
42
|
+
*/
|
|
43
|
+
export default function HeaderBlockRenderer({ block }: BlockComponentProps) {
|
|
44
|
+
const { actionButtons, logoHref, logoImage, logoText, menuItems } = block as HeaderBlock
|
|
45
|
+
|
|
46
|
+
const pathname = usePathname()
|
|
47
|
+
// Same predicate the `/admin` route runs server-side, so this link can never
|
|
48
|
+
// offer a destination that redirects to `/unauthorized`. Reading a role off
|
|
49
|
+
// the session by hand was the previous approach and never matched: roles are
|
|
50
|
+
// stored in `access.userRolesField` ("roles"), not better-auth's scalar
|
|
51
|
+
// `user.role`, so the button rendered for nobody.
|
|
52
|
+
const canAccessAdmin = useCanAccessAdminPanel()
|
|
53
|
+
|
|
33
54
|
const [isScrolled, setIsScrolled] = useState(false)
|
|
55
|
+
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
|
34
56
|
|
|
35
57
|
useEffect(() => {
|
|
58
|
+
let frame = 0
|
|
36
59
|
const handleScroll = () => {
|
|
37
|
-
|
|
60
|
+
if (frame) {return}
|
|
61
|
+
frame = window.requestAnimationFrame(() => {
|
|
62
|
+
frame = 0
|
|
63
|
+
setIsScrolled(window.scrollY > SCROLL_THRESHOLD)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
handleScroll()
|
|
67
|
+
window.addEventListener("scroll", handleScroll, { passive: true })
|
|
68
|
+
return () => {
|
|
69
|
+
window.removeEventListener("scroll", handleScroll)
|
|
70
|
+
window.cancelAnimationFrame(frame)
|
|
38
71
|
}
|
|
39
|
-
window.addEventListener("scroll", handleScroll)
|
|
40
|
-
return () => window.removeEventListener("scroll", handleScroll)
|
|
41
72
|
}, [])
|
|
42
73
|
|
|
74
|
+
const items = menuItems ?? []
|
|
75
|
+
const actions = actionButtons ?? []
|
|
76
|
+
const isCrowded = items.length >= NAV_CROWD_THRESHOLD
|
|
77
|
+
// The whole bar switches at one breakpoint: `md` normally, `lg` when the nav
|
|
78
|
+
// is crowded. Tailwind needs both class sets written out literally.
|
|
79
|
+
const navClass = isCrowded ? "hidden lg:flex" : "hidden md:flex"
|
|
80
|
+
const triggerClass = isCrowded ? "lg:hidden" : "md:hidden"
|
|
81
|
+
|
|
43
82
|
return (
|
|
44
|
-
<header
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{
|
|
64
|
-
|
|
83
|
+
<header
|
|
84
|
+
className={cn(
|
|
85
|
+
"sticky top-0 z-50 transition-[background-color,border-color,height] duration-[180ms] ease-[var(--ease-emphasized)]",
|
|
86
|
+
isScrolled
|
|
87
|
+
? "h-14 border-b border-border bg-background/70 backdrop-blur-md"
|
|
88
|
+
: "h-14 border-b border-transparent md:h-16"
|
|
89
|
+
)}
|
|
90
|
+
>
|
|
91
|
+
<Container className="flex h-full items-center justify-between gap-6">
|
|
92
|
+
<div className="flex items-center gap-10">
|
|
93
|
+
<Link
|
|
94
|
+
aria-label={logoText ?? "Home"}
|
|
95
|
+
className="flex shrink-0 items-center gap-2 text-foreground"
|
|
96
|
+
href={logoHref ?? "/"}
|
|
97
|
+
>
|
|
98
|
+
{logoImage?.length ? (
|
|
99
|
+
<MediaImage
|
|
100
|
+
alt={logoText ?? ""}
|
|
101
|
+
className="h-5 w-auto"
|
|
102
|
+
height={20}
|
|
103
|
+
value={logoImage}
|
|
104
|
+
width={96}
|
|
105
|
+
/>
|
|
106
|
+
) : (
|
|
107
|
+
<>
|
|
108
|
+
<BrandMark className="size-4 text-primary" />
|
|
109
|
+
{logoText ? (
|
|
110
|
+
<span className="text-[15px] font-extrabold tracking-[-0.03em]">{logoText}</span>
|
|
111
|
+
) : null}
|
|
112
|
+
</>
|
|
113
|
+
)}
|
|
114
|
+
</Link>
|
|
65
115
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</div>
|
|
116
|
+
{items.length > 0 ? (
|
|
117
|
+
<nav className={cn("items-center gap-1", navClass)}>
|
|
118
|
+
{items.map((item) => {
|
|
119
|
+
const isCurrent = pathname === item.href
|
|
120
|
+
return (
|
|
121
|
+
<Link
|
|
122
|
+
aria-current={isCurrent ? "page" : undefined}
|
|
123
|
+
className={cn(
|
|
124
|
+
"rounded-sm px-2.5 py-1.5 text-sm transition-colors duration-[180ms] ease-[var(--ease-emphasized)] active:translate-y-px",
|
|
125
|
+
isCurrent
|
|
126
|
+
? "bg-accent text-foreground"
|
|
127
|
+
: "text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
128
|
+
)}
|
|
129
|
+
href={item.href}
|
|
130
|
+
key={`${item.label}-${item.href}`}
|
|
131
|
+
>
|
|
132
|
+
{item.label}
|
|
133
|
+
</Link>
|
|
134
|
+
)
|
|
135
|
+
})}
|
|
136
|
+
</nav>
|
|
137
|
+
) : null}
|
|
138
|
+
</div>
|
|
90
139
|
|
|
91
|
-
|
|
92
|
-
|
|
140
|
+
<div className="flex items-center gap-2">
|
|
141
|
+
{actions.length > 0 ? (
|
|
142
|
+
<div className={cn("items-center gap-2", navClass)}>
|
|
143
|
+
{actions.map((action) => (
|
|
93
144
|
<Link
|
|
94
|
-
key={index}
|
|
95
|
-
href={button.href}
|
|
96
145
|
className={cn(
|
|
97
146
|
buttonVariants({
|
|
98
147
|
size: "sm",
|
|
99
|
-
variant:
|
|
100
|
-
|
|
148
|
+
variant:
|
|
149
|
+
(action.variant?.[0]) ??
|
|
150
|
+
"default",
|
|
151
|
+
}),
|
|
152
|
+
"active:translate-y-px"
|
|
101
153
|
)}
|
|
154
|
+
href={action.href}
|
|
155
|
+
key={`${action.label}-${action.href}`}
|
|
102
156
|
>
|
|
103
|
-
|
|
157
|
+
{action.label}
|
|
104
158
|
</Link>
|
|
105
159
|
))}
|
|
106
|
-
{isAdmin && (
|
|
107
|
-
<Link
|
|
108
|
-
href="/admin"
|
|
109
|
-
className={cn(
|
|
110
|
-
buttonVariants({ size: "sm", variant: "outline" }),
|
|
111
|
-
"gap-1.5"
|
|
112
|
-
)}
|
|
113
|
-
>
|
|
114
|
-
<Settings className="size-3.5" />
|
|
115
|
-
<span>Admin</span>
|
|
116
|
-
</Link>
|
|
117
|
-
)}
|
|
118
160
|
</div>
|
|
161
|
+
) : null}
|
|
119
162
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
163
|
+
{/* Template affordance, not part of the marketing design: a signed-in
|
|
164
|
+
admin gets a direct route to the panel even when the seeded nav
|
|
165
|
+
does not list one. */}
|
|
166
|
+
{canAccessAdmin ? (
|
|
167
|
+
<Link
|
|
168
|
+
className={cn(
|
|
169
|
+
buttonVariants({ size: "sm", variant: "outline" }),
|
|
170
|
+
navClass,
|
|
171
|
+
"gap-1.5 active:translate-y-px"
|
|
172
|
+
)}
|
|
173
|
+
href="/admin"
|
|
174
|
+
>
|
|
175
|
+
<Settings className="size-3.5" />
|
|
176
|
+
Admin
|
|
177
|
+
</Link>
|
|
178
|
+
) : null}
|
|
179
|
+
|
|
180
|
+
{items.length > 0 || actions.length > 0 ? (
|
|
181
|
+
<Sheet onOpenChange={setIsMenuOpen} open={isMenuOpen}>
|
|
182
|
+
<SheetTrigger
|
|
183
|
+
aria-label="Open menu"
|
|
184
|
+
className={cn(buttonVariants({ size: "icon-sm", variant: "ghost" }), triggerClass)}
|
|
185
|
+
>
|
|
186
|
+
<Menu className="size-5" />
|
|
187
|
+
</SheetTrigger>
|
|
188
|
+
<SheetContent className="rounded-lg border-border bg-background" side="top">
|
|
189
|
+
<SheetTitle className="sr-only">Menu</SheetTitle>
|
|
190
|
+
<nav className="flex flex-col px-2 pt-2">
|
|
191
|
+
{items.map((item) => (
|
|
192
|
+
<Link
|
|
193
|
+
aria-current={pathname === item.href ? "page" : undefined}
|
|
194
|
+
className={cn(
|
|
195
|
+
"flex min-h-11 items-center rounded-sm px-3 text-[15px] transition-colors duration-[180ms] active:translate-y-px",
|
|
196
|
+
pathname === item.href
|
|
197
|
+
? "bg-accent text-foreground"
|
|
198
|
+
: "text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
199
|
+
)}
|
|
200
|
+
href={item.href}
|
|
201
|
+
key={`sheet-${item.label}-${item.href}`}
|
|
202
|
+
onClick={() => setIsMenuOpen(false)}
|
|
203
|
+
>
|
|
204
|
+
{item.label}
|
|
205
|
+
</Link>
|
|
133
206
|
))}
|
|
134
|
-
</
|
|
135
|
-
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
)}
|
|
164
|
-
</div>
|
|
165
|
-
)}
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
207
|
+
</nav>
|
|
208
|
+
|
|
209
|
+
{actions.length > 0 ? (
|
|
210
|
+
<>
|
|
211
|
+
<Separator className="my-4" />
|
|
212
|
+
<div className="flex flex-col gap-2 px-2 pb-2">
|
|
213
|
+
{actions.map((action) => (
|
|
214
|
+
<Link
|
|
215
|
+
className={cn(
|
|
216
|
+
buttonVariants({
|
|
217
|
+
size: "default",
|
|
218
|
+
variant:
|
|
219
|
+
(action.variant?.[0]) ?? "default",
|
|
220
|
+
}),
|
|
221
|
+
"w-full"
|
|
222
|
+
)}
|
|
223
|
+
href={action.href}
|
|
224
|
+
key={`sheet-${action.label}-${action.href}`}
|
|
225
|
+
onClick={() => setIsMenuOpen(false)}
|
|
226
|
+
>
|
|
227
|
+
{action.label}
|
|
228
|
+
</Link>
|
|
229
|
+
))}
|
|
230
|
+
</div>
|
|
231
|
+
</>
|
|
232
|
+
) : null}
|
|
233
|
+
</SheetContent>
|
|
234
|
+
</Sheet>
|
|
235
|
+
) : null}
|
|
169
236
|
</div>
|
|
170
|
-
</
|
|
237
|
+
</Container>
|
|
171
238
|
</header>
|
|
172
239
|
)
|
|
173
240
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineBlock, text } from "@vexcms/core"
|
|
1
|
+
import { defineBlock, select, text } from "@vexcms/core"
|
|
2
2
|
|
|
3
3
|
import { BLOCK_SLUG_HERO } from "../constants"
|
|
4
4
|
|
|
@@ -6,35 +6,50 @@ export const heroBlock = defineBlock({
|
|
|
6
6
|
slug: BLOCK_SLUG_HERO,
|
|
7
7
|
label: "Hero Section",
|
|
8
8
|
fields: {
|
|
9
|
+
variant: select({
|
|
10
|
+
label: "Variant",
|
|
11
|
+
description:
|
|
12
|
+
"Full is the landing hero — 90vh, centred, decorative background. Compact is an interior page-header band.",
|
|
13
|
+
options: [
|
|
14
|
+
{ label: "Full", value: "full" },
|
|
15
|
+
{ label: "Compact", value: "compact" },
|
|
16
|
+
],
|
|
17
|
+
defaultValue: ["full"],
|
|
18
|
+
}),
|
|
9
19
|
badgeText: text({
|
|
10
20
|
label: "Badge Text",
|
|
11
|
-
defaultValue: "
|
|
12
|
-
description:
|
|
21
|
+
defaultValue: "v0.1.0-alpha — now on npm",
|
|
22
|
+
description:
|
|
23
|
+
"Announcement pill on the full variant; the uppercase eyebrow on the compact one.",
|
|
13
24
|
}),
|
|
14
25
|
badgeLink: text({
|
|
15
26
|
label: "Badge Link",
|
|
16
|
-
defaultValue: "/
|
|
17
|
-
description: "
|
|
27
|
+
defaultValue: "https://www.npmjs.com/package/@vexcms/core",
|
|
28
|
+
description: "Optional. Without it the badge renders as plain text, not a link.",
|
|
18
29
|
}),
|
|
19
30
|
heading: text({
|
|
20
31
|
label: "Heading",
|
|
21
32
|
required: true,
|
|
22
|
-
defaultValue: "The CMS
|
|
33
|
+
defaultValue: "The CMS that thinks in types.",
|
|
23
34
|
}),
|
|
24
35
|
subheading: text({
|
|
25
36
|
label: "Subheading",
|
|
26
37
|
defaultValue:
|
|
27
|
-
"
|
|
38
|
+
"A headless CMS built natively on Convex. Declare your collections in TypeScript and Vex generates the Convex schema, the types, and the queries — no translation layer. Every edit reaches every subscriber in milliseconds.",
|
|
39
|
+
}),
|
|
40
|
+
installCommand: text({
|
|
41
|
+
label: "Install Command",
|
|
42
|
+
defaultValue: "pnpm create vexcms@alpha",
|
|
43
|
+
description:
|
|
44
|
+
"Optional. Renders a copy-to-clipboard command row under the CTAs. Full variant only.",
|
|
28
45
|
}),
|
|
29
46
|
primaryCtaLabel: text({
|
|
30
47
|
label: "Primary CTA Label",
|
|
31
|
-
|
|
32
|
-
defaultValue: "Get Started",
|
|
48
|
+
defaultValue: "Read the docs",
|
|
33
49
|
}),
|
|
34
50
|
primaryCtaHref: text({
|
|
35
51
|
label: "Primary CTA Link",
|
|
36
|
-
|
|
37
|
-
defaultValue: "/docs",
|
|
52
|
+
defaultValue: "https://docs.vexcms.dev/guides/quickstart/",
|
|
38
53
|
}),
|
|
39
54
|
secondaryCtaLabel: text({
|
|
40
55
|
label: "Secondary CTA Label",
|
|
@@ -42,7 +57,7 @@ export const heroBlock = defineBlock({
|
|
|
42
57
|
}),
|
|
43
58
|
secondaryCtaHref: text({
|
|
44
59
|
label: "Secondary CTA Link",
|
|
45
|
-
defaultValue: "https://github.com/
|
|
60
|
+
defaultValue: "https://github.com/ianyimi/vex",
|
|
46
61
|
}),
|
|
47
62
|
},
|
|
48
63
|
admin: {
|