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
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
/* eslint-disable perfectionist/sort-union-types */
|
|
5
5
|
/* eslint-disable perfectionist/sort-interfaces */
|
|
6
6
|
/* eslint-disable perfectionist/sort-modules */
|
|
7
|
+
/* eslint-disable perfectionist/sort-object-types */
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
7
9
|
|
|
8
10
|
import type { Id } from "@convex/_generated/dataModel"
|
|
9
11
|
import type { VexDocument, VexDocumentGlobal } from "@vexcms/core"
|
|
@@ -28,16 +30,35 @@ export type HeroBlock = {
|
|
|
28
30
|
blockType: "hero"
|
|
29
31
|
blockName?: string
|
|
30
32
|
id: string
|
|
33
|
+
variant?: ("full" | "compact")[]
|
|
31
34
|
badgeText?: string
|
|
32
35
|
badgeLink?: string
|
|
33
36
|
heading: string
|
|
34
37
|
subheading?: string
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
installCommand?: string
|
|
39
|
+
primaryCtaLabel?: string
|
|
40
|
+
primaryCtaHref?: string
|
|
37
41
|
secondaryCtaLabel?: string
|
|
38
42
|
secondaryCtaHref?: string
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
export type StatsBlock = {
|
|
46
|
+
blockType: "stats"
|
|
47
|
+
blockName?: string
|
|
48
|
+
id: string
|
|
49
|
+
heading?: string
|
|
50
|
+
subheading?: string
|
|
51
|
+
items: {
|
|
52
|
+
/**
|
|
53
|
+
* Free text, not a number — "0", "12" and "~30s" all render. Any unit belongs in
|
|
54
|
+
* the string.
|
|
55
|
+
*/
|
|
56
|
+
value: string
|
|
57
|
+
label: string
|
|
58
|
+
description?: string
|
|
59
|
+
}[]
|
|
60
|
+
}
|
|
61
|
+
|
|
41
62
|
export type FeaturesBlock = {
|
|
42
63
|
blockType: "features"
|
|
43
64
|
blockName?: string
|
|
@@ -54,6 +75,51 @@ export type FeaturesBlock = {
|
|
|
54
75
|
}[]
|
|
55
76
|
}
|
|
56
77
|
|
|
78
|
+
export type CodeShowcaseBlock = {
|
|
79
|
+
blockType: "code_showcase"
|
|
80
|
+
blockName?: string
|
|
81
|
+
id: string
|
|
82
|
+
heading: string
|
|
83
|
+
subheading?: string
|
|
84
|
+
panes: {
|
|
85
|
+
/**
|
|
86
|
+
* Shown uppercase in the chrome bar.
|
|
87
|
+
*/
|
|
88
|
+
label: string
|
|
89
|
+
filename?: string
|
|
90
|
+
language?: ("ts" | "tsx" | "bash" | "json")[]
|
|
91
|
+
/**
|
|
92
|
+
* Hand-written panes carry the primary-coloured label — the only signal separating
|
|
93
|
+
* human input from generated output.
|
|
94
|
+
*/
|
|
95
|
+
authored?: ("authored" | "generated")[]
|
|
96
|
+
code: string
|
|
97
|
+
caption?: string
|
|
98
|
+
}[]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type SplitBlock = {
|
|
102
|
+
blockType: "split"
|
|
103
|
+
blockName?: string
|
|
104
|
+
id: string
|
|
105
|
+
eyebrow?: string
|
|
106
|
+
heading: string
|
|
107
|
+
body: string
|
|
108
|
+
bullets?: {
|
|
109
|
+
/**
|
|
110
|
+
* Lucide icon name
|
|
111
|
+
*/
|
|
112
|
+
icon?: string
|
|
113
|
+
text: string
|
|
114
|
+
}[]
|
|
115
|
+
media?: ("code" | "image" | "none")[]
|
|
116
|
+
code?: string
|
|
117
|
+
codeFilename?: string
|
|
118
|
+
codeLanguage?: ("ts" | "tsx" | "bash" | "json")[]
|
|
119
|
+
image?: Id<"images">[]
|
|
120
|
+
mediaPosition?: ("right" | "left")[]
|
|
121
|
+
}
|
|
122
|
+
|
|
57
123
|
export type HowItWorksBlock = {
|
|
58
124
|
blockType: "how_it_works"
|
|
59
125
|
blockName?: string
|
|
@@ -79,37 +145,45 @@ export type RoadmapBlock = {
|
|
|
79
145
|
items: {
|
|
80
146
|
feature: string
|
|
81
147
|
description?: string
|
|
82
|
-
status:
|
|
148
|
+
status: ("shipped" | "in-progress" | "planned" | "exploring" | "future")[]
|
|
83
149
|
}[]
|
|
84
150
|
}
|
|
85
151
|
|
|
86
|
-
export type
|
|
87
|
-
blockType: "
|
|
152
|
+
export type FaqBlock = {
|
|
153
|
+
blockType: "faq"
|
|
88
154
|
blockName?: string
|
|
89
155
|
id: string
|
|
90
156
|
heading: string
|
|
91
157
|
subheading?: string
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
158
|
+
supportLink?: string
|
|
159
|
+
items: {
|
|
160
|
+
question: string
|
|
161
|
+
answer: string
|
|
95
162
|
}[]
|
|
96
163
|
}
|
|
97
164
|
|
|
98
|
-
export type
|
|
99
|
-
blockType: "
|
|
165
|
+
export type CtaBlock = {
|
|
166
|
+
blockType: "cta"
|
|
100
167
|
blockName?: string
|
|
101
168
|
id: string
|
|
102
169
|
heading: string
|
|
103
170
|
subheading?: string
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
answer: string
|
|
171
|
+
actions?: {
|
|
172
|
+
label: string
|
|
173
|
+
href: string
|
|
108
174
|
}[]
|
|
109
175
|
}
|
|
110
176
|
|
|
111
177
|
export type PageBlock =
|
|
112
|
-
|
|
178
|
+
| HeroBlock
|
|
179
|
+
| StatsBlock
|
|
180
|
+
| FeaturesBlock
|
|
181
|
+
| CodeShowcaseBlock
|
|
182
|
+
| SplitBlock
|
|
183
|
+
| HowItWorksBlock
|
|
184
|
+
| RoadmapBlock
|
|
185
|
+
| FaqBlock
|
|
186
|
+
| CtaBlock
|
|
113
187
|
|
|
114
188
|
export interface PagesDocument extends VexDocument {
|
|
115
189
|
_id: Id<"pages">
|
|
@@ -128,6 +202,7 @@ export interface PagesDocument extends VexDocument {
|
|
|
128
202
|
*/
|
|
129
203
|
metaDescription?: string
|
|
130
204
|
ogImage?: Id<"images">[]
|
|
205
|
+
updatedAt?: number
|
|
131
206
|
}
|
|
132
207
|
export type HeaderBlock = {
|
|
133
208
|
blockType: "header"
|
|
@@ -143,7 +218,7 @@ export type HeaderBlock = {
|
|
|
143
218
|
actionButtons?: {
|
|
144
219
|
label: string
|
|
145
220
|
href: string
|
|
146
|
-
variant?:
|
|
221
|
+
variant?: ("default" | "outline" | "ghost")[]
|
|
147
222
|
}[]
|
|
148
223
|
}
|
|
149
224
|
|
|
@@ -151,6 +226,7 @@ export interface HeadersDocument extends VexDocument {
|
|
|
151
226
|
_id: Id<"headers">
|
|
152
227
|
name: string
|
|
153
228
|
content?: HeaderBlock[]
|
|
229
|
+
updatedAt?: number
|
|
154
230
|
}
|
|
155
231
|
export type FooterBlock = {
|
|
156
232
|
blockType: "footer"
|
|
@@ -177,6 +253,7 @@ export interface FootersDocument extends VexDocument {
|
|
|
177
253
|
_id: Id<"footers">
|
|
178
254
|
name: string
|
|
179
255
|
content?: FooterBlock[]
|
|
256
|
+
updatedAt?: number
|
|
180
257
|
}
|
|
181
258
|
|
|
182
259
|
export interface Theme extends VexDocument {
|
|
@@ -269,6 +346,7 @@ export interface Theme extends VexDocument {
|
|
|
269
346
|
sidebarBorder?: string
|
|
270
347
|
sidebarRing?: string
|
|
271
348
|
}
|
|
349
|
+
updatedAt?: number
|
|
272
350
|
}
|
|
273
351
|
|
|
274
352
|
export interface SessionDocument extends VexDocument {
|
|
@@ -281,7 +359,6 @@ export interface SessionDocument extends VexDocument {
|
|
|
281
359
|
userAgent?: string
|
|
282
360
|
userId: string
|
|
283
361
|
impersonatedBy?: string
|
|
284
|
-
activeOrganizationId?: string
|
|
285
362
|
}
|
|
286
363
|
|
|
287
364
|
export interface AccountDocument extends VexDocument {
|
|
@@ -342,34 +419,6 @@ export interface JwksDocument extends VexDocument {
|
|
|
342
419
|
expiresAt?: number
|
|
343
420
|
}
|
|
344
421
|
|
|
345
|
-
export interface OrganizationDocument extends VexDocument {
|
|
346
|
-
_id: Id<"organization">
|
|
347
|
-
name: string
|
|
348
|
-
slug: string
|
|
349
|
-
logo?: string
|
|
350
|
-
createdAt: number
|
|
351
|
-
metadata?: string
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export interface MemberDocument extends VexDocument {
|
|
355
|
-
_id: Id<"member">
|
|
356
|
-
organizationId: string
|
|
357
|
-
userId: string
|
|
358
|
-
role: string
|
|
359
|
-
createdAt: number
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
export interface InvitationDocument extends VexDocument {
|
|
363
|
-
_id: Id<"invitation">
|
|
364
|
-
organizationId: string
|
|
365
|
-
email: string
|
|
366
|
-
role?: string
|
|
367
|
-
status: string
|
|
368
|
-
expiresAt: number
|
|
369
|
-
createdAt: number
|
|
370
|
-
inviterId: string
|
|
371
|
-
}
|
|
372
|
-
|
|
373
422
|
export interface Image extends VexDocument {
|
|
374
423
|
_id: Id<"images">
|
|
375
424
|
filename: string
|
|
@@ -381,6 +430,7 @@ export interface Image extends VexDocument {
|
|
|
381
430
|
src: string
|
|
382
431
|
width?: number
|
|
383
432
|
height?: number
|
|
433
|
+
updatedAt?: number
|
|
384
434
|
}
|
|
385
435
|
|
|
386
436
|
export interface SiteSettingsGlobal extends VexDocumentGlobal<"siteSettings"> {
|
|
@@ -434,9 +484,6 @@ export type CollectionSlug =
|
|
|
434
484
|
| "verification"
|
|
435
485
|
| "apikey"
|
|
436
486
|
| "jwks"
|
|
437
|
-
| "organization"
|
|
438
|
-
| "member"
|
|
439
|
-
| "invitation"
|
|
440
487
|
| "images"
|
|
441
488
|
|
|
442
489
|
export type GlobalSlug = "siteSettings"
|
|
@@ -456,9 +503,6 @@ export type DocumentBySlug = {
|
|
|
456
503
|
verification: VerificationDocument
|
|
457
504
|
apikey: ApikeyDocument
|
|
458
505
|
jwks: JwksDocument
|
|
459
|
-
organization: OrganizationDocument
|
|
460
|
-
member: MemberDocument
|
|
461
|
-
invitation: InvitationDocument
|
|
462
506
|
images: Image
|
|
463
507
|
}
|
|
464
508
|
|
|
@@ -479,9 +523,6 @@ declare module "@vexcms/core" {
|
|
|
479
523
|
| "verification"
|
|
480
524
|
| "apikey"
|
|
481
525
|
| "jwks"
|
|
482
|
-
| "organization"
|
|
483
|
-
| "member"
|
|
484
|
-
| "invitation"
|
|
485
526
|
| "images"
|
|
486
527
|
GlobalSlug: "siteSettings"
|
|
487
528
|
MediaCollectionSlug: "images"
|
|
@@ -497,9 +538,6 @@ declare module "@vexcms/core" {
|
|
|
497
538
|
verification: VerificationDocument
|
|
498
539
|
apikey: ApikeyDocument
|
|
499
540
|
jwks: JwksDocument
|
|
500
|
-
organization: OrganizationDocument
|
|
501
|
-
member: MemberDocument
|
|
502
|
-
invitation: InvitationDocument
|
|
503
541
|
images: Image
|
|
504
542
|
}
|
|
505
543
|
GlobalDocumentBySlug: {
|
|
@@ -516,23 +554,26 @@ declare module "@vexcms/core" {
|
|
|
516
554
|
text: "title" | "slug" | "metaTitle" | "metaDescription"
|
|
517
555
|
blocks: "blocks"
|
|
518
556
|
upload: "ogImage"
|
|
557
|
+
number: "updatedAt"
|
|
519
558
|
}
|
|
520
559
|
headers: {
|
|
521
560
|
text: "name"
|
|
522
561
|
blocks: "content"
|
|
562
|
+
number: "updatedAt"
|
|
523
563
|
}
|
|
524
564
|
footers: {
|
|
525
565
|
text: "name"
|
|
526
566
|
blocks: "content"
|
|
567
|
+
number: "updatedAt"
|
|
527
568
|
}
|
|
528
569
|
themes: {
|
|
529
570
|
text: "name" | "fontFamily" | "radius"
|
|
530
571
|
group: "light" | "dark"
|
|
572
|
+
number: "updatedAt"
|
|
531
573
|
}
|
|
532
574
|
session: {
|
|
533
575
|
date: "expiresAt" | "createdAt" | "updatedAt"
|
|
534
|
-
text:
|
|
535
|
-
"token" | "ipAddress" | "userAgent" | "userId" | "impersonatedBy" | "activeOrganizationId"
|
|
576
|
+
text: "token" | "ipAddress" | "userAgent" | "userId" | "impersonatedBy"
|
|
536
577
|
}
|
|
537
578
|
account: {
|
|
538
579
|
text:
|
|
@@ -574,21 +615,9 @@ declare module "@vexcms/core" {
|
|
|
574
615
|
text: "publicKey" | "privateKey"
|
|
575
616
|
date: "createdAt" | "expiresAt"
|
|
576
617
|
}
|
|
577
|
-
organization: {
|
|
578
|
-
text: "name" | "slug" | "logo" | "metadata"
|
|
579
|
-
date: "createdAt"
|
|
580
|
-
}
|
|
581
|
-
member: {
|
|
582
|
-
text: "organizationId" | "userId" | "role"
|
|
583
|
-
date: "createdAt"
|
|
584
|
-
}
|
|
585
|
-
invitation: {
|
|
586
|
-
text: "organizationId" | "email" | "role" | "status" | "inviterId"
|
|
587
|
-
date: "expiresAt" | "createdAt"
|
|
588
|
-
}
|
|
589
618
|
images: {
|
|
590
619
|
text: "filename" | "alt" | "mimeType" | "storageId" | "src"
|
|
591
|
-
number: "size" | "width" | "height"
|
|
620
|
+
number: "size" | "width" | "height" | "updatedAt"
|
|
592
621
|
checkbox: "deleted"
|
|
593
622
|
}
|
|
594
623
|
}
|
|
@@ -620,15 +649,12 @@ declare module "@vexcms/core" {
|
|
|
620
649
|
by_key: readonly ["key"]
|
|
621
650
|
}
|
|
622
651
|
jwks: {}
|
|
623
|
-
organization: { by_slug: readonly ["slug"] }
|
|
624
|
-
member: { by_organizationId: readonly ["organizationId"]; by_userId: readonly ["userId"] }
|
|
625
|
-
invitation: { by_organizationId: readonly ["organizationId"]; by_email: readonly ["email"] }
|
|
626
652
|
images: { by_deleted: readonly ["deleted"] }
|
|
627
653
|
}
|
|
628
654
|
CustomActionsBySlug: {}
|
|
629
655
|
AuthSlugs: {
|
|
630
656
|
user: "user"
|
|
631
|
-
organization:
|
|
657
|
+
organization: never
|
|
632
658
|
}
|
|
633
659
|
}
|
|
634
660
|
}
|
|
@@ -3,7 +3,7 @@ import { createGetAuth } from "@vexcms/better-auth"
|
|
|
3
3
|
import { vexServerApi } from "@vexcms/core/server"
|
|
4
4
|
|
|
5
5
|
import { TABLE_SLUG_ORGANIZATIONS, TABLE_SLUG_SESSIONS, TABLE_SLUG_USERS } from "~/db/constants"
|
|
6
|
-
import config from "~/vex.config"
|
|
6
|
+
import config from "~/vex.config.server"
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Bound server API — `config`/`getAuth` are threaded once here so
|
|
@@ -12,7 +12,7 @@ import config from "~/vex.config"
|
|
|
12
12
|
*
|
|
13
13
|
* Mirrors `apps/test/src/vexcms/api.ts`'s `vexServerApi` binding.
|
|
14
14
|
*/
|
|
15
|
-
export const { get, find, search, create, remove, update, globals } = vexServerApi<DataModel>({
|
|
15
|
+
export const { get, find, search, create, remove, update, globals, publishedSlugs } = vexServerApi<DataModel>({
|
|
16
16
|
config,
|
|
17
17
|
getAuth: createGetAuth({
|
|
18
18
|
orgCollectionSlug: TABLE_SLUG_ORGANIZATIONS,
|
|
@@ -19,14 +19,15 @@ export const ctaBlock = defineBlock({
|
|
|
19
19
|
actions: array({
|
|
20
20
|
label: "Actions",
|
|
21
21
|
items: group({
|
|
22
|
+
label: "Action",
|
|
22
23
|
fields: {
|
|
23
24
|
label: text({ label: "Button Label", required: true }),
|
|
24
25
|
href: text({ label: "Button Link", required: true }),
|
|
25
26
|
},
|
|
26
27
|
}),
|
|
27
28
|
defaultValue: [
|
|
28
|
-
{ label: "Get Started", href: "/
|
|
29
|
-
{ label: "View on GitHub", href: "https://github.com/
|
|
29
|
+
{ label: "Get Started", href: "https://docs.vexcms.dev/guides/quickstart/" },
|
|
30
|
+
{ label: "View on GitHub", href: "https://github.com/ianyimi/vex" },
|
|
30
31
|
],
|
|
31
32
|
}),
|
|
32
33
|
},
|
|
@@ -1,46 +1,62 @@
|
|
|
1
1
|
import type { BlockComponentProps } from "@vexcms/react"
|
|
2
2
|
|
|
3
|
+
import { buttonVariants, cn } from "@vexcms/react"
|
|
3
4
|
import Link from "next/link"
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
6
|
+
import type { CtaBlock } from "~/vex.types"
|
|
7
|
+
|
|
8
|
+
import { Container } from "~/components/Container"
|
|
7
9
|
|
|
8
10
|
export { ctaBlock } from "./config"
|
|
9
11
|
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* CTA — the page's last surface.
|
|
15
|
+
*
|
|
16
|
+
* The only block permitted more air than a standard section: it is what
|
|
17
|
+
* separates the page from the footer. Shares `bg-card` with Stats, so the two
|
|
18
|
+
* must never sit adjacent.
|
|
19
|
+
*
|
|
20
|
+
* `actions` has no variant field, so the renderer assigns by index — first
|
|
21
|
+
* solid, the rest outline.
|
|
22
|
+
*/
|
|
10
23
|
export default function CTABlock({ block }: BlockComponentProps) {
|
|
11
|
-
const { heading, subheading
|
|
12
|
-
|
|
13
|
-
subheading?: string
|
|
14
|
-
actions?: Array<{ label: string; href: string }>
|
|
15
|
-
}
|
|
24
|
+
const { actions, heading, subheading } = block as CtaBlock
|
|
25
|
+
const items = actions ?? []
|
|
16
26
|
|
|
17
27
|
return (
|
|
18
|
-
<section className="py-16 md:py-32">
|
|
19
|
-
<
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
<section className="border-y border-border bg-card py-16 text-center md:py-24 xl:py-32">
|
|
29
|
+
<Container>
|
|
30
|
+
<h2 className="mx-auto max-w-[24ch] text-[28px] leading-[1.12] font-bold tracking-[-0.03em] text-balance text-foreground md:text-4xl xl:text-[40px]">
|
|
31
|
+
{heading}
|
|
32
|
+
</h2>
|
|
33
|
+
|
|
34
|
+
{subheading ? (
|
|
35
|
+
<p className="mx-auto mt-5 max-w-[60ch] text-[17px] leading-[1.6] text-pretty text-muted-foreground">
|
|
36
|
+
{subheading}
|
|
37
|
+
</p>
|
|
38
|
+
) : null}
|
|
39
|
+
|
|
40
|
+
{items.length > 0 ? (
|
|
41
|
+
<div className="mx-auto mt-9 flex w-full max-w-[320px] flex-col gap-3 sm:max-w-none sm:flex-row sm:flex-wrap sm:justify-center">
|
|
42
|
+
{items.map((action, index) => (
|
|
28
43
|
<Link
|
|
29
|
-
key={index}
|
|
30
|
-
href={action.href ?? "/"}
|
|
31
44
|
className={cn(
|
|
32
45
|
buttonVariants({
|
|
33
46
|
size: "lg",
|
|
34
|
-
variant: index
|
|
35
|
-
})
|
|
47
|
+
variant: index === 0 ? "default" : "outline",
|
|
48
|
+
}),
|
|
49
|
+
"active:translate-y-px"
|
|
36
50
|
)}
|
|
51
|
+
href={action.href}
|
|
52
|
+
key={`${action.label}-${index}`}
|
|
37
53
|
>
|
|
38
|
-
|
|
54
|
+
{action.label}
|
|
39
55
|
</Link>
|
|
40
56
|
))}
|
|
41
57
|
</div>
|
|
42
|
-
|
|
43
|
-
</
|
|
58
|
+
) : null}
|
|
59
|
+
</Container>
|
|
44
60
|
</section>
|
|
45
61
|
)
|
|
46
62
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { array, defineBlock, group, select, text } from "@vexcms/core"
|
|
2
|
+
|
|
3
|
+
import { BLOCK_SLUG_CODE_SHOWCASE } from "../constants"
|
|
4
|
+
|
|
5
|
+
export const codeShowcaseBlock = defineBlock({
|
|
6
|
+
slug: BLOCK_SLUG_CODE_SHOWCASE,
|
|
7
|
+
label: "Code Showcase",
|
|
8
|
+
fields: {
|
|
9
|
+
heading: text({
|
|
10
|
+
label: "Heading",
|
|
11
|
+
required: true,
|
|
12
|
+
defaultValue: "Write the collection. Get the database.",
|
|
13
|
+
}),
|
|
14
|
+
subheading: text({
|
|
15
|
+
label: "Subheading",
|
|
16
|
+
defaultValue:
|
|
17
|
+
"The left pane is hand-written; the right one is the Convex schema vex dev emits from it and never edited.",
|
|
18
|
+
}),
|
|
19
|
+
panes: array({
|
|
20
|
+
label: "Panes",
|
|
21
|
+
required: true,
|
|
22
|
+
description:
|
|
23
|
+
"Two panes render side by side in one frame. Three or more render as a tab set.",
|
|
24
|
+
items: group({
|
|
25
|
+
label: "Pane",
|
|
26
|
+
fields: {
|
|
27
|
+
label: text({
|
|
28
|
+
label: "Pane Label",
|
|
29
|
+
required: true,
|
|
30
|
+
description: "Shown uppercase in the chrome bar.",
|
|
31
|
+
}),
|
|
32
|
+
filename: text({ label: "Filename" }),
|
|
33
|
+
language: select({
|
|
34
|
+
label: "Language",
|
|
35
|
+
options: [
|
|
36
|
+
{ label: "TypeScript", value: "ts" },
|
|
37
|
+
{ label: "TSX", value: "tsx" },
|
|
38
|
+
{ label: "Shell", value: "bash" },
|
|
39
|
+
{ label: "JSON", value: "json" },
|
|
40
|
+
],
|
|
41
|
+
defaultValue: ["ts"],
|
|
42
|
+
}),
|
|
43
|
+
authored: select({
|
|
44
|
+
label: "Origin",
|
|
45
|
+
description:
|
|
46
|
+
"Hand-written panes carry the primary-coloured label — the only signal separating human input from generated output.",
|
|
47
|
+
options: [
|
|
48
|
+
{ label: "Hand-written", value: "authored" },
|
|
49
|
+
{ label: "Generated", value: "generated" },
|
|
50
|
+
],
|
|
51
|
+
defaultValue: ["generated"],
|
|
52
|
+
}),
|
|
53
|
+
code: text({ label: "Code", required: true }),
|
|
54
|
+
caption: text({ label: "Caption" }),
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
}),
|
|
58
|
+
},
|
|
59
|
+
admin: {
|
|
60
|
+
icon: "FileCode",
|
|
61
|
+
},
|
|
62
|
+
})
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { BlockComponentProps } from "@vexcms/react"
|
|
2
|
+
|
|
3
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@vexcms/react"
|
|
4
|
+
import { Fragment } from "react"
|
|
5
|
+
|
|
6
|
+
import type { CodeShowcaseBlock } from "~/vex.types"
|
|
7
|
+
|
|
8
|
+
import { CodePane } from "~/components/CodePane"
|
|
9
|
+
import { Container } from "~/components/Container"
|
|
10
|
+
import { SectionHeader } from "~/components/SectionHeader"
|
|
11
|
+
|
|
12
|
+
export { codeShowcaseBlock } from "./config"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* CodeShowcase — the config, and the file it wrote.
|
|
18
|
+
*
|
|
19
|
+
* Two panes share one frame with a single seam, so the pairing reads as one
|
|
20
|
+
* artefact rather than two cards. Three or more become a tab set inside the
|
|
21
|
+
* same frame. Zero panes render nothing — an empty frame is worse than no
|
|
22
|
+
* section.
|
|
23
|
+
*/
|
|
24
|
+
export default function CodeShowcaseBlock({ block }: BlockComponentProps) {
|
|
25
|
+
const { heading, panes, subheading } = block as CodeShowcaseBlock
|
|
26
|
+
const items = panes ?? []
|
|
27
|
+
|
|
28
|
+
if (items.length === 0) {return null}
|
|
29
|
+
|
|
30
|
+
const hasCaption = items.some((pane) => Boolean(pane.caption))
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<section className="py-14 md:py-20 xl:py-28">
|
|
34
|
+
<Container>
|
|
35
|
+
<SectionHeader heading={heading} subheading={subheading} />
|
|
36
|
+
|
|
37
|
+
{items.length > 2 ? (
|
|
38
|
+
<Tabs className="overflow-hidden rounded-md border border-border" defaultValue="0">
|
|
39
|
+
<TabsList className="h-10 w-full justify-start rounded-none border-b border-border bg-card px-2">
|
|
40
|
+
{items.map((pane, index) => (
|
|
41
|
+
<TabsTrigger
|
|
42
|
+
className="rounded-sm px-3 py-[5px] text-[13px] font-medium"
|
|
43
|
+
key={`${pane.label}-${index}`}
|
|
44
|
+
value={String(index)}
|
|
45
|
+
>
|
|
46
|
+
{pane.label}
|
|
47
|
+
</TabsTrigger>
|
|
48
|
+
))}
|
|
49
|
+
</TabsList>
|
|
50
|
+
{items.map((pane, index) => (
|
|
51
|
+
<TabsContent key={`${pane.label}-panel-${index}`} value={String(index)}>
|
|
52
|
+
<CodePane
|
|
53
|
+
code={pane.code}
|
|
54
|
+
filename={pane.filename}
|
|
55
|
+
language={pane.language}
|
|
56
|
+
tone={readTone(pane.authored)}
|
|
57
|
+
/>
|
|
58
|
+
</TabsContent>
|
|
59
|
+
))}
|
|
60
|
+
</Tabs>
|
|
61
|
+
) : (
|
|
62
|
+
// The 1px middle track is the seam. `minmax(0,1fr)` on the pane
|
|
63
|
+
// columns is what lets the panes scroll instead of overflowing.
|
|
64
|
+
<div className="grid items-stretch overflow-hidden rounded-md border border-border xl:grid-cols-[minmax(0,1fr)_1px_minmax(0,1fr)]">
|
|
65
|
+
{items.map((pane, index) => (
|
|
66
|
+
// The keyed Fragment is what React needs: keys on the children
|
|
67
|
+
// inside a bare `<>` do not count, so this logged "Each child in
|
|
68
|
+
// a list should have a unique key prop" on every render.
|
|
69
|
+
<Fragment key={`${pane.label}-${index}`}>
|
|
70
|
+
{index > 0 ? (
|
|
71
|
+
<div aria-hidden className="bg-border max-xl:h-px xl:w-px" />
|
|
72
|
+
) : null}
|
|
73
|
+
<CodePane
|
|
74
|
+
code={pane.code}
|
|
75
|
+
filename={pane.filename}
|
|
76
|
+
label={pane.label}
|
|
77
|
+
language={pane.language}
|
|
78
|
+
tone={readTone(pane.authored)}
|
|
79
|
+
/>
|
|
80
|
+
</Fragment>
|
|
81
|
+
))}
|
|
82
|
+
</div>
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
{hasCaption ? (
|
|
86
|
+
<div className="mt-3 grid gap-3 xl:grid-cols-[minmax(0,1fr)_1px_minmax(0,1fr)]">
|
|
87
|
+
{items.map((pane, index) => (
|
|
88
|
+
<Fragment key={`caption-${pane.label}-${index}`}>
|
|
89
|
+
{index > 0 ? <div aria-hidden /> : null}
|
|
90
|
+
<p className="text-[13px] leading-[1.55] text-muted-foreground">
|
|
91
|
+
{pane.caption ?? ""}
|
|
92
|
+
</p>
|
|
93
|
+
</Fragment>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
) : null}
|
|
97
|
+
</Container>
|
|
98
|
+
</section>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** `select` stores an array; `[0]` with a literal fallback survives both. */
|
|
103
|
+
function readTone(value: string | string[] | undefined): "authored" | "generated" {
|
|
104
|
+
const raw = Array.isArray(value) ? value[0] : value
|
|
105
|
+
return raw === "authored" ? "authored" : "generated"
|
|
106
|
+
}
|
|
@@ -18,13 +18,14 @@ export const faqBlock = defineBlock({
|
|
|
18
18
|
}),
|
|
19
19
|
supportLink: text({
|
|
20
20
|
label: "Support Link",
|
|
21
|
-
defaultValue: "https://github.com/
|
|
21
|
+
defaultValue: "https://github.com/ianyimi/vex/issues",
|
|
22
22
|
description: "URL to contact support page",
|
|
23
23
|
}),
|
|
24
24
|
items: array({
|
|
25
25
|
label: "FAQ Items",
|
|
26
26
|
required: true,
|
|
27
27
|
items: group({
|
|
28
|
+
label: "Question",
|
|
28
29
|
fields: {
|
|
29
30
|
question: text({ label: "Question", required: true }),
|
|
30
31
|
answer: text({ label: "Answer", required: true }),
|
|
@@ -34,7 +35,7 @@ export const faqBlock = defineBlock({
|
|
|
34
35
|
{
|
|
35
36
|
question: "What is Vex CMS?",
|
|
36
37
|
answer:
|
|
37
|
-
"Vex CMS is a headless content management system built on Convex. It provides real-time data, type-safe schemas,
|
|
38
|
+
"Vex CMS is a headless content management system built on Convex. It provides real-time data, type-safe schemas, database-driven theming, role-based access control, and a full admin panel — all configured in TypeScript. Versioning, drafts, and live preview are in progress; see the roadmap.",
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
41
|
question: "How is Vex different from other headless CMS platforms?",
|
|
@@ -59,7 +60,7 @@ export const faqBlock = defineBlock({
|
|
|
59
60
|
{
|
|
60
61
|
question: "How do I get started?",
|
|
61
62
|
answer:
|
|
62
|
-
"Run `npx create-vexcms@
|
|
63
|
+
"Run `npx create-vexcms@alpha` to scaffold a new project with Vex CMS pre-configured. The CLI sets up your Next.js app, Convex backend, authentication, and admin panel in under a minute.",
|
|
63
64
|
},
|
|
64
65
|
],
|
|
65
66
|
}),
|