bsmnt 0.0.0

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.
Files changed (98) hide show
  1. package/.changeset/2026-02-11-test-patch-bump.md +5 -0
  2. package/.changeset/README.md +10 -0
  3. package/.changeset/config.json +16 -0
  4. package/.cursor/rules/README.md +184 -0
  5. package/.cursor/rules/architecture.mdc +437 -0
  6. package/.cursor/rules/components.mdc +436 -0
  7. package/.cursor/rules/integrations.mdc +447 -0
  8. package/.cursor/rules/main.mdc +278 -0
  9. package/.cursor/rules/styling.mdc +433 -0
  10. package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  11. package/.github/workflows/.gitkeep +0 -0
  12. package/.github/workflows/ci.yml +37 -0
  13. package/.github/workflows/release.yml +54 -0
  14. package/.tldr/cache/call_graph.json +7 -0
  15. package/.tldr/languages.json +6 -0
  16. package/.tldr/status +1 -0
  17. package/.tldrignore +84 -0
  18. package/.vscode/extensions.json +20 -0
  19. package/.vscode/settings.json +98 -0
  20. package/CHANGELOG.md +13 -0
  21. package/CLAUDE.md +138 -0
  22. package/README.md +176 -0
  23. package/bin/index.js +262 -0
  24. package/biome.json +44 -0
  25. package/bun.lock +496 -0
  26. package/changelog/04-02-26.md +86 -0
  27. package/changelog/05-02-26.md +101 -0
  28. package/changelog/09-02-26.md +83 -0
  29. package/docs/fix-studio-hydration.md +46 -0
  30. package/docs/plans/2026-01-29-sanity-smart-merge-design.md +196 -0
  31. package/docs/plans/2026-01-29-sanity-smart-merge-implementation.md +695 -0
  32. package/docs/sanity-setup-steps.md +199 -0
  33. package/integrations/basehub/README.md +3 -0
  34. package/integrations/sanity/app/api/draft-mode/disable/route.ts +7 -0
  35. package/integrations/sanity/app/api/draft-mode/enable/route.ts +21 -0
  36. package/integrations/sanity/app/api/revalidate/route.ts +37 -0
  37. package/integrations/sanity/app/layout.tsx +111 -0
  38. package/integrations/sanity/app/sitemap.ts +80 -0
  39. package/integrations/sanity/app/studio/[[...tool]]/page.tsx +8 -0
  40. package/integrations/sanity/app/studio/layout.tsx +7 -0
  41. package/integrations/sanity/components/ui/sanity-image/index.tsx +37 -0
  42. package/integrations/sanity/lib/integrations/README.md +58 -0
  43. package/integrations/sanity/lib/integrations/check-integration.ts +62 -0
  44. package/integrations/sanity/lib/integrations/sanity/README.md +144 -0
  45. package/integrations/sanity/lib/integrations/sanity/client.ts +30 -0
  46. package/integrations/sanity/lib/integrations/sanity/components/disable-draft-mode.tsx +29 -0
  47. package/integrations/sanity/lib/integrations/sanity/components/rich-text.tsx +73 -0
  48. package/integrations/sanity/lib/integrations/sanity/env.ts +38 -0
  49. package/integrations/sanity/lib/integrations/sanity/live/index.tsx +34 -0
  50. package/integrations/sanity/lib/integrations/sanity/queries.ts +99 -0
  51. package/integrations/sanity/lib/integrations/sanity/sanity.cli.ts +20 -0
  52. package/integrations/sanity/lib/integrations/sanity/sanity.config.ts +94 -0
  53. package/integrations/sanity/lib/integrations/sanity/sanity.types.ts +337 -0
  54. package/integrations/sanity/lib/integrations/sanity/schema.json +1850 -0
  55. package/integrations/sanity/lib/integrations/sanity/schemas/article.ts +132 -0
  56. package/integrations/sanity/lib/integrations/sanity/schemas/example.ts +203 -0
  57. package/integrations/sanity/lib/integrations/sanity/schemas/index.ts +37 -0
  58. package/integrations/sanity/lib/integrations/sanity/schemas/link.ts +127 -0
  59. package/integrations/sanity/lib/integrations/sanity/schemas/metadata.ts +68 -0
  60. package/integrations/sanity/lib/integrations/sanity/schemas/navigation.ts +39 -0
  61. package/integrations/sanity/lib/integrations/sanity/schemas/page.ts +77 -0
  62. package/integrations/sanity/lib/integrations/sanity/schemas/richText.ts +59 -0
  63. package/integrations/sanity/lib/integrations/sanity/structure.ts +5 -0
  64. package/integrations/sanity/lib/integrations/sanity/utils/image.ts +11 -0
  65. package/integrations/sanity/lib/integrations/sanity/utils/link.ts +61 -0
  66. package/integrations/sanity/lib/scripts/copy-sanity-mcp.ts +23 -0
  67. package/integrations/sanity/lib/scripts/generate-page.ts +310 -0
  68. package/integrations/sanity/lib/utils/metadata.ts +190 -0
  69. package/layers/experiment/components/layout/header/index.tsx +58 -0
  70. package/layers/experiment/components/layout/navigation-menu.tsx +127 -0
  71. package/layers/experiment/lib/constants.ts +12 -0
  72. package/layers/webgl/app/page.tsx +10 -0
  73. package/layers/webgl/components/webgl/canvas/dynamic.tsx +34 -0
  74. package/layers/webgl/components/webgl/canvas/index.tsx +43 -0
  75. package/layers/webgl/components/webgl/components/scene/index.tsx +21 -0
  76. package/layers/webgpu/.gitkeep +0 -0
  77. package/package.json +44 -0
  78. package/plugins/README.md +21 -0
  79. package/plugins/no-anchor-element.grit +11 -0
  80. package/plugins/no-relative-parent-imports.grit +6 -0
  81. package/plugins/no-unnecessary-forwardref.grit +5 -0
  82. package/src/commands/add-integration.js +325 -0
  83. package/src/commands/create.js +415 -0
  84. package/src/commands/setup-sanity.js +426 -0
  85. package/src/commands/worktree.js +805 -0
  86. package/src/mergers/check-integration-merger.js +105 -0
  87. package/src/mergers/config.js +137 -0
  88. package/src/mergers/index.js +355 -0
  89. package/src/mergers/layout-merger.js +223 -0
  90. package/src/mergers/next-config-merger.js +63 -0
  91. package/src/mergers/sitemap-merger.js +121 -0
  92. package/tasks/prd-next-starter-dynamic-layers.md +184 -0
  93. package/tasks/prd.json +153 -0
  94. package/tasks/progress.txt +115 -0
  95. package/template-hooks/use-battery.ts +126 -0
  96. package/template-hooks/use-device-perf.ts +184 -0
  97. package/template-hooks/use-intersection-observer.ts +32 -0
  98. package/template-hooks/use-media.ts +33 -0
@@ -0,0 +1,132 @@
1
+ import { defineField, defineType } from "sanity";
2
+
3
+ const articleFields = [
4
+ defineField({
5
+ name: "title",
6
+ title: "Title",
7
+ type: "string",
8
+ description: "The title of the article",
9
+ validation: (Rule) => Rule.required(),
10
+ }),
11
+ defineField({
12
+ name: "excerpt",
13
+ title: "Excerpt",
14
+ type: "text",
15
+ rows: 3,
16
+ description: "A brief summary of the article",
17
+ validation: (Rule) => Rule.max(200),
18
+ }),
19
+ defineField({
20
+ name: "featuredImage",
21
+ title: "Featured Image",
22
+ type: "image",
23
+ description: "Main image for the article",
24
+ options: {
25
+ hotspot: true,
26
+ },
27
+ fields: [
28
+ {
29
+ name: "alt",
30
+ title: "Alt Text",
31
+ type: "string",
32
+ description: "Alternative text for screen readers",
33
+ },
34
+ ],
35
+ }),
36
+ defineField({
37
+ name: "content",
38
+ title: "Content",
39
+ type: "richText",
40
+ description: "The main content of the article",
41
+ }),
42
+ defineField({
43
+ name: "categories",
44
+ title: "Categories",
45
+ type: "array",
46
+ of: [{ type: "string" }],
47
+ description: "Categories for this article",
48
+ options: {
49
+ layout: "tags",
50
+ },
51
+ }),
52
+ defineField({
53
+ name: "tags",
54
+ title: "Tags",
55
+ type: "array",
56
+ of: [{ type: "string" }],
57
+ description: "Tags for this article",
58
+ options: {
59
+ layout: "tags",
60
+ },
61
+ }),
62
+ ];
63
+
64
+ export const article = defineType({
65
+ name: "article",
66
+ title: "Article",
67
+ type: "document",
68
+ fields: [
69
+ defineField({
70
+ name: "slug",
71
+ title: "Slug",
72
+ type: "slug",
73
+ description: "The URL path for this article",
74
+ options: {
75
+ source: "title",
76
+ maxLength: 96,
77
+ },
78
+ validation: (Rule) => Rule.required(),
79
+ }),
80
+ defineField({
81
+ name: "author",
82
+ title: "Author",
83
+ type: "string",
84
+ description: "The author of this article",
85
+ }),
86
+ defineField({
87
+ name: "publishedAt",
88
+ title: "Published At",
89
+ type: "datetime",
90
+ description: "When this article was published",
91
+ initialValue: () => new Date().toISOString(),
92
+ }),
93
+ defineField({
94
+ name: "metadata",
95
+ title: "SEO & Metadata",
96
+ type: "metadata",
97
+ description: "SEO settings for this article",
98
+ }),
99
+ ...articleFields,
100
+ ],
101
+ preview: {
102
+ select: {
103
+ title: "title",
104
+ slug: "slug.current",
105
+ media: "featuredImage",
106
+ },
107
+ prepare({ title, slug, media }) {
108
+ return {
109
+ title: title || "Untitled",
110
+ subtitle: slug ? `/blog/${slug}` : "No slug",
111
+ media,
112
+ };
113
+ },
114
+ },
115
+ orderings: [
116
+ {
117
+ title: "Published Date, New",
118
+ name: "publishedAtDesc",
119
+ by: [{ field: "publishedAt", direction: "desc" }],
120
+ },
121
+ {
122
+ title: "Published Date, Old",
123
+ name: "publishedAtAsc",
124
+ by: [{ field: "publishedAt", direction: "asc" }],
125
+ },
126
+ {
127
+ title: "Title A-Z",
128
+ name: "titleAsc",
129
+ by: [{ field: "title", direction: "asc" }],
130
+ },
131
+ ],
132
+ });
@@ -0,0 +1,203 @@
1
+ import { defineField, defineType } from "sanity";
2
+
3
+ /**
4
+ * Example schema demonstrating common patterns and best practices
5
+ * This can be used as a reference for creating new content types
6
+ */
7
+ export const example = defineType({
8
+ name: "example",
9
+ title: "Example Page",
10
+ type: "document",
11
+ fields: [
12
+ // Basic required fields
13
+ defineField({
14
+ name: "title",
15
+ title: "Title",
16
+ type: "string",
17
+ description: "The main title of the page",
18
+ validation: (Rule) => Rule.required().max(60),
19
+ }),
20
+ defineField({
21
+ name: "slug",
22
+ title: "Slug",
23
+ type: "slug",
24
+ description: "URL path for this page",
25
+ options: {
26
+ source: "title",
27
+ maxLength: 96,
28
+ },
29
+ validation: (Rule) => Rule.required(),
30
+ }),
31
+
32
+ // Hero section object
33
+ defineField({
34
+ name: "hero",
35
+ title: "Hero Section",
36
+ type: "object",
37
+ fields: [
38
+ {
39
+ name: "headline",
40
+ title: "Headline",
41
+ type: "string",
42
+ validation: (Rule) => Rule.max(100),
43
+ },
44
+ {
45
+ name: "subheadline",
46
+ title: "Subheadline",
47
+ type: "text",
48
+ rows: 3,
49
+ },
50
+ {
51
+ name: "image",
52
+ title: "Hero Image",
53
+ type: "image",
54
+ options: {
55
+ hotspot: true,
56
+ },
57
+ fields: [
58
+ {
59
+ name: "alt",
60
+ title: "Alt Text",
61
+ type: "string",
62
+ description: "Alternative text for screen readers",
63
+ },
64
+ ],
65
+ },
66
+ {
67
+ name: "showCTA",
68
+ title: "Show Call to Action",
69
+ type: "boolean",
70
+ initialValue: false,
71
+ },
72
+ {
73
+ name: "ctaText",
74
+ title: "CTA Text",
75
+ type: "string",
76
+ hidden: ({ parent }) => !parent?.showCTA,
77
+ },
78
+ {
79
+ name: "ctaLink",
80
+ title: "CTA Link",
81
+ type: "url",
82
+ hidden: ({ parent }) => !parent?.showCTA,
83
+ },
84
+ ],
85
+ }),
86
+
87
+ // Rich text content
88
+ defineField({
89
+ name: "content",
90
+ title: "Content",
91
+ type: "richText",
92
+ description: "The main content of the page",
93
+ }),
94
+
95
+ // Features array
96
+ defineField({
97
+ name: "features",
98
+ title: "Features",
99
+ type: "array",
100
+ of: [
101
+ {
102
+ type: "object",
103
+ fields: [
104
+ {
105
+ name: "title",
106
+ title: "Feature Title",
107
+ type: "string",
108
+ },
109
+ {
110
+ name: "description",
111
+ title: "Description",
112
+ type: "text",
113
+ rows: 3,
114
+ },
115
+ {
116
+ name: "icon",
117
+ title: "Icon",
118
+ type: "image",
119
+ options: {
120
+ hotspot: true,
121
+ },
122
+ },
123
+ ],
124
+ },
125
+ ],
126
+ }),
127
+
128
+ // Tags array
129
+ defineField({
130
+ name: "tags",
131
+ title: "Tags",
132
+ type: "array",
133
+ of: [{ type: "string" }],
134
+ options: {
135
+ layout: "tags",
136
+ },
137
+ }),
138
+
139
+ // SEO metadata
140
+ defineField({
141
+ name: "metadata",
142
+ title: "SEO & Metadata",
143
+ type: "metadata",
144
+ description: "SEO settings for this page",
145
+ }),
146
+
147
+ // Publishing info
148
+ defineField({
149
+ name: "publishedAt",
150
+ title: "Published At",
151
+ type: "datetime",
152
+ description: "When this page was published",
153
+ initialValue: () => new Date().toISOString(),
154
+ }),
155
+
156
+ // Conditional field example
157
+ defineField({
158
+ name: "showInNavigation",
159
+ title: "Show in Navigation",
160
+ type: "boolean",
161
+ initialValue: false,
162
+ }),
163
+
164
+ // Custom validation example
165
+ defineField({
166
+ name: "contactEmail",
167
+ title: "Contact Email",
168
+ type: "string",
169
+ validation: (Rule) =>
170
+ Rule.email().error("Please enter a valid email address"),
171
+ }),
172
+ ],
173
+
174
+ // Custom preview
175
+ preview: {
176
+ select: {
177
+ title: "title",
178
+ slug: "slug.current",
179
+ media: "hero.image",
180
+ },
181
+ prepare({ title, slug, media }) {
182
+ return {
183
+ title: title || "Untitled Example",
184
+ subtitle: slug ? `/${slug}` : "No slug",
185
+ media,
186
+ };
187
+ },
188
+ },
189
+
190
+ // Ordering options
191
+ orderings: [
192
+ {
193
+ title: "Published Date, New",
194
+ name: "publishedAtDesc",
195
+ by: [{ field: "publishedAt", direction: "desc" }],
196
+ },
197
+ {
198
+ title: "Title A-Z",
199
+ name: "titleAsc",
200
+ by: [{ field: "title", direction: "asc" }],
201
+ },
202
+ ],
203
+ });
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Sanity Schema Types
3
+ *
4
+ * All schema definitions for Sanity CMS, organized in a flat structure.
5
+ * This replaces the previous documents/objects/singletons directory structure.
6
+ */
7
+
8
+ import type { SchemaTypeDefinition } from "sanity";
9
+ import { article } from "./article";
10
+ import { example } from "./example";
11
+ // Import all schema definitions
12
+ import { link } from "./link";
13
+ import { metadata } from "./metadata";
14
+ import { navigation } from "./navigation";
15
+ import { page } from "./page";
16
+ import { richText } from "./richText";
17
+
18
+ // Re-export all schemas for convenience
19
+ export { link, metadata, richText, article, example, page, navigation };
20
+
21
+ // Schema collection for Sanity configuration
22
+ export const schema: { types: SchemaTypeDefinition[] } = {
23
+ types: [
24
+ // Object types (reusable components)
25
+ link,
26
+ metadata,
27
+ richText,
28
+
29
+ // Document types (content pages)
30
+ page,
31
+ article,
32
+ example,
33
+
34
+ // Singleton types (one-off content)
35
+ navigation,
36
+ ],
37
+ };
@@ -0,0 +1,127 @@
1
+ import { defineField, defineType } from "sanity";
2
+
3
+ // Native Sanity link object type
4
+ export const link = defineType({
5
+ name: "link",
6
+ title: "Link",
7
+ type: "object",
8
+ fields: [
9
+ defineField({
10
+ name: "linkType",
11
+ title: "Link Type",
12
+ type: "string",
13
+ options: {
14
+ list: [
15
+ { title: "Internal", value: "internal" },
16
+ { title: "External", value: "external" },
17
+ ],
18
+ layout: "radio",
19
+ },
20
+ initialValue: "internal",
21
+ validation: (Rule) => Rule.required(),
22
+ }),
23
+ defineField({
24
+ name: "internalLink",
25
+ title: "Internal Link",
26
+ type: "reference",
27
+ to: [{ type: "page" }, { type: "article" }],
28
+ hidden: ({ parent }) => parent?.linkType !== "internal",
29
+ validation: (Rule) =>
30
+ Rule.custom((value, context) => {
31
+ const parent = context.parent as { linkType?: string };
32
+ if (parent?.linkType === "internal" && !value) {
33
+ return "Internal link is required";
34
+ }
35
+ return true;
36
+ }),
37
+ }),
38
+ defineField({
39
+ name: "externalUrl",
40
+ title: "External URL",
41
+ type: "url",
42
+ hidden: ({ parent }) => parent?.linkType !== "external",
43
+ validation: (Rule) =>
44
+ Rule.custom((value, context) => {
45
+ const parent = context.parent as { linkType?: string };
46
+ if (parent?.linkType === "external" && !value) {
47
+ return "External URL is required";
48
+ }
49
+ return true;
50
+ }).uri({
51
+ scheme: ["http", "https", "mailto", "tel"],
52
+ }),
53
+ }),
54
+ defineField({
55
+ name: "text",
56
+ title: "Link Text",
57
+ type: "string",
58
+ description: "Optional custom text for the link",
59
+ }),
60
+ defineField({
61
+ name: "openInNewTab",
62
+ title: "Open in New Tab",
63
+ type: "boolean",
64
+ initialValue: false,
65
+ hidden: ({ parent }) => parent?.linkType !== "external",
66
+ }),
67
+ ],
68
+ preview: {
69
+ select: {
70
+ linkType: "linkType",
71
+ text: "text",
72
+ internalTitle: "internalLink.title",
73
+ externalUrl: "externalUrl",
74
+ },
75
+ prepare({ linkType, text, internalTitle, externalUrl }) {
76
+ const title = text || internalTitle || externalUrl || "Untitled Link";
77
+ const subtitle =
78
+ linkType === "internal"
79
+ ? `Internal: ${internalTitle || "No page selected"}`
80
+ : `External: ${externalUrl || "No URL"}`;
81
+
82
+ return {
83
+ title,
84
+ subtitle,
85
+ };
86
+ },
87
+ },
88
+ });
89
+
90
+ // Helper field exports for easy reuse
91
+ export const linkField = defineField({
92
+ name: "link",
93
+ title: "Link",
94
+ type: "link",
95
+ });
96
+
97
+ export const linkFieldWithLabel = defineField({
98
+ name: "link",
99
+ title: "Link",
100
+ type: "link",
101
+ });
102
+
103
+ export const linkFieldWithLabelAndRequired = defineField({
104
+ name: "link",
105
+ title: "Link",
106
+ type: "link",
107
+ validation: (Rule) => Rule.required(),
108
+ });
109
+
110
+ type LinkFieldOptions = {
111
+ name?: string;
112
+ title?: string;
113
+ required?: boolean;
114
+ };
115
+
116
+ export function extendedLinkField({
117
+ name = "link",
118
+ title = "Link",
119
+ required = false,
120
+ }: LinkFieldOptions = {}) {
121
+ return defineField({
122
+ name,
123
+ title,
124
+ type: "link",
125
+ validation: required ? (Rule) => Rule.required() : undefined,
126
+ });
127
+ }
@@ -0,0 +1,68 @@
1
+ import { defineField, defineType } from "sanity";
2
+
3
+ export const metadata = defineType({
4
+ name: "metadata",
5
+ title: "SEO & Metadata",
6
+ type: "object",
7
+ options: {
8
+ collapsible: true,
9
+ collapsed: true,
10
+ },
11
+ fields: [
12
+ defineField({
13
+ name: "title",
14
+ title: "Meta Title",
15
+ type: "string",
16
+ description: "Title for search engines and social media",
17
+ validation: (Rule) =>
18
+ Rule.max(60).warning(
19
+ "Title should be under 60 characters for optimal SEO",
20
+ ),
21
+ }),
22
+ defineField({
23
+ name: "description",
24
+ title: "Meta Description",
25
+ type: "text",
26
+ rows: 3,
27
+ description: "Description for search engines and social media",
28
+ validation: (Rule) =>
29
+ Rule.max(160).warning(
30
+ "Description should be under 160 characters for optimal SEO",
31
+ ),
32
+ }),
33
+ defineField({
34
+ name: "keywords",
35
+ title: "Keywords",
36
+ type: "array",
37
+ of: [{ type: "string" }],
38
+ description: "Keywords for search engines",
39
+ options: {
40
+ layout: "tags",
41
+ },
42
+ }),
43
+ defineField({
44
+ name: "image",
45
+ title: "Social Media Image",
46
+ type: "image",
47
+ description: "Image for social media sharing (1200x630px recommended)",
48
+ options: {
49
+ hotspot: true,
50
+ },
51
+ fields: [
52
+ {
53
+ name: "alt",
54
+ title: "Alt Text",
55
+ type: "string",
56
+ description: "Alternative text for screen readers",
57
+ },
58
+ ],
59
+ }),
60
+ defineField({
61
+ name: "noIndex",
62
+ title: "No Index",
63
+ type: "boolean",
64
+ description: "Prevent search engines from indexing this page",
65
+ initialValue: false,
66
+ }),
67
+ ],
68
+ });
@@ -0,0 +1,39 @@
1
+ import { defineField, defineType } from "sanity";
2
+
3
+ export const navigation = defineType({
4
+ name: "navigation",
5
+ title: "Navigation",
6
+ type: "document",
7
+ fields: [
8
+ defineField({
9
+ name: "title",
10
+ type: "string",
11
+ title: "Title",
12
+ description: "Title of the navigation",
13
+ }),
14
+ defineField({
15
+ type: "array",
16
+ name: "socials",
17
+ title: "Socials",
18
+ of: [
19
+ {
20
+ name: "socialLink",
21
+ type: "object",
22
+ title: "Social Link",
23
+ fields: [
24
+ defineField({
25
+ name: "logo",
26
+ type: "image",
27
+ title: "Logo",
28
+ }),
29
+ defineField({
30
+ name: "socialMedia",
31
+ title: "Social Media",
32
+ type: "link",
33
+ }),
34
+ ],
35
+ },
36
+ ],
37
+ }),
38
+ ],
39
+ });
@@ -0,0 +1,77 @@
1
+ import { defineField, defineType } from "sanity";
2
+ import { linkFieldWithLabelAndRequired } from "./link";
3
+
4
+ const pageFields = [
5
+ defineField({
6
+ name: "title",
7
+ title: "Title",
8
+ type: "string",
9
+ description: "The title of the page",
10
+ validation: (Rule) => Rule.required(),
11
+ }),
12
+ defineField({
13
+ name: "content",
14
+ title: "Content",
15
+ type: "richText",
16
+ description: "The main content of the page",
17
+ }),
18
+ linkFieldWithLabelAndRequired,
19
+ ];
20
+
21
+ export const page = defineType({
22
+ name: "page",
23
+ title: "Page",
24
+ type: "document",
25
+ fields: [
26
+ defineField({
27
+ name: "slug",
28
+ title: "Slug",
29
+ type: "slug",
30
+ description: "The URL path for this page",
31
+ options: {
32
+ source: "title",
33
+ maxLength: 96,
34
+ },
35
+ validation: (Rule) => Rule.required(),
36
+ }),
37
+ defineField({
38
+ name: "publishedAt",
39
+ title: "Published At",
40
+ type: "datetime",
41
+ description: "When this page was published",
42
+ initialValue: () => new Date().toISOString(),
43
+ }),
44
+
45
+ defineField({
46
+ name: "metadata",
47
+ title: "SEO & Metadata",
48
+ type: "metadata",
49
+ description: "SEO settings for this page",
50
+ }),
51
+ ...pageFields,
52
+ ],
53
+ preview: {
54
+ select: {
55
+ title: "title",
56
+ slug: "slug.current",
57
+ },
58
+ prepare({ title, slug }) {
59
+ return {
60
+ title: title || "Untitled",
61
+ subtitle: slug ? `/${slug}` : "No slug",
62
+ };
63
+ },
64
+ },
65
+ orderings: [
66
+ {
67
+ title: "Published Date, New",
68
+ name: "publishedAtDesc",
69
+ by: [{ field: "publishedAt", direction: "desc" }],
70
+ },
71
+ {
72
+ title: "Published Date, Old",
73
+ name: "publishedAtAsc",
74
+ by: [{ field: "publishedAt", direction: "asc" }],
75
+ },
76
+ ],
77
+ });