sanity-plugin-seofields 1.0.2 → 1.0.3
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 +9 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +5 -3
- package/src/plugin.ts +9 -3
- package/src/schemas/index.ts +4 -3
- package/src/utils/fieldsUtils.ts +1 -1
package/README.md
CHANGED
|
@@ -201,7 +201,14 @@ Each field in the `fieldOverrides` object can have:
|
|
|
201
201
|
- `title` - Custom title for the field
|
|
202
202
|
- `description` - Custom description/help text for the field
|
|
203
203
|
|
|
204
|
-
Available field keys
|
|
204
|
+
**Available field keys:**
|
|
205
|
+
|
|
206
|
+
- `title`
|
|
207
|
+
- `description`
|
|
208
|
+
- `canonicalUrl`
|
|
209
|
+
- `metaImage`
|
|
210
|
+
- `keywords`
|
|
211
|
+
- `metaAttributes`
|
|
205
212
|
|
|
206
213
|
### Field Specifications
|
|
207
214
|
|
|
@@ -284,7 +291,7 @@ const seoData: SeoFields = {
|
|
|
284
291
|
import type {
|
|
285
292
|
SeoFields,
|
|
286
293
|
SeoFieldsPluginConfig,
|
|
287
|
-
|
|
294
|
+
SeoFieldConfig,
|
|
288
295
|
SeoFieldKeys,
|
|
289
296
|
OpenGraphSettings,
|
|
290
297
|
TwitterCardSettings,
|
package/dist/index.d.mts
CHANGED
|
@@ -154,7 +154,7 @@ export declare interface SanityImageWithAlt extends SanityImage {
|
|
|
154
154
|
alt: string
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
export declare interface
|
|
157
|
+
export declare interface SeoFieldConfig {
|
|
158
158
|
title?: string
|
|
159
159
|
description?: string
|
|
160
160
|
}
|
|
@@ -165,6 +165,7 @@ export declare type SeoFieldKeys =
|
|
|
165
165
|
| 'canonicalUrl'
|
|
166
166
|
| 'metaImage'
|
|
167
167
|
| 'keywords'
|
|
168
|
+
| 'metaAttributes'
|
|
168
169
|
|
|
169
170
|
export declare interface SeoFields {
|
|
170
171
|
_type: 'seoFields'
|
|
@@ -185,7 +186,7 @@ export default seofields
|
|
|
185
186
|
export declare interface SeoFieldsPluginConfig {
|
|
186
187
|
seoPreview?: boolean
|
|
187
188
|
fieldOverrides?: {
|
|
188
|
-
[key in SeoFieldKeys]?:
|
|
189
|
+
[key in SeoFieldKeys]?: SeoFieldConfig
|
|
189
190
|
}
|
|
190
191
|
}
|
|
191
192
|
|
package/dist/index.d.ts
CHANGED
|
@@ -154,7 +154,7 @@ export declare interface SanityImageWithAlt extends SanityImage {
|
|
|
154
154
|
alt: string
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
export declare interface
|
|
157
|
+
export declare interface SeoFieldConfig {
|
|
158
158
|
title?: string
|
|
159
159
|
description?: string
|
|
160
160
|
}
|
|
@@ -165,6 +165,7 @@ export declare type SeoFieldKeys =
|
|
|
165
165
|
| 'canonicalUrl'
|
|
166
166
|
| 'metaImage'
|
|
167
167
|
| 'keywords'
|
|
168
|
+
| 'metaAttributes'
|
|
168
169
|
|
|
169
170
|
export declare interface SeoFields {
|
|
170
171
|
_type: 'seoFields'
|
|
@@ -185,7 +186,7 @@ export default seofields
|
|
|
185
186
|
export declare interface SeoFieldsPluginConfig {
|
|
186
187
|
seoPreview?: boolean
|
|
187
188
|
fieldOverrides?: {
|
|
188
|
-
[key in SeoFieldKeys]?:
|
|
189
|
+
[key in SeoFieldKeys]?: SeoFieldConfig
|
|
189
190
|
}
|
|
190
191
|
}
|
|
191
192
|
|
package/dist/index.js
CHANGED
|
@@ -370,10 +370,12 @@ function seoFieldsSchema(config = {}) {
|
|
|
370
370
|
}),
|
|
371
371
|
sanity.defineField({
|
|
372
372
|
name: "metaAttributes",
|
|
373
|
-
title:
|
|
373
|
+
// title: 'Additional Meta Attributes',
|
|
374
|
+
...getFieldInfo("metaAttributes", config.fieldOverrides),
|
|
374
375
|
type: "array",
|
|
375
|
-
of: [{ type: "metaAttribute" }]
|
|
376
|
-
description:
|
|
376
|
+
of: [{ type: "metaAttribute" }]
|
|
377
|
+
// description:
|
|
378
|
+
// 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
|
|
377
379
|
}),
|
|
378
380
|
sanity.defineField({
|
|
379
381
|
name: "keywords",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/utils/seoUtils.ts","../src/components/meta/MetaTitle.tsx","../src/components/meta/MetaDescription.tsx","../src/components/SeoPreview.tsx","../src/utils/fieldsUtils.ts","../src/schemas/index.ts","../src/schemas/types/metaAttribute/index.ts","../src/components/openGraph/OgTitle.tsx","../src/components/openGraph/OgDescription.tsx","../src/schemas/types/openGraph/index.ts","../src/components/twitter/twitterTitle.tsx","../src/components/twitter/twitterDescription.tsx","../src/schemas/types/twitter/index.ts","../src/schemas/types/robots/index.ts","../src/schemas/types/metaTag/index.ts","../src/schemas/types/index.ts","../src/plugin.ts","../src/types.ts"],"sourcesContent":["import {PathSegment, useFormValue} from 'sanity'\n\nexport const stopWords = ['the', 'a', 'an', 'and', 'or', 'but']\n\nexport const hasMatchingKeyword = (title: string, keywordList: string[]): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()))\n}\n\nexport const hasKeywordOveruse = (\n title: string,\n keywordList: string[],\n maxOccurrences = 3,\n): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => {\n if (!keyword) return false\n const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), 'g'))\n return matches ? matches.length > maxOccurrences : false\n })\n}\n\nexport const startsWithStopWord = (title: string): boolean => {\n if (!title) return false\n const firstWord = title.trim().split(' ')[0].toLowerCase()\n return stopWords.includes(firstWord)\n}\n\nexport const primaryKeywordAtStart = (title: string, keywords: string[]): boolean => {\n if (!title || keywords.length === 0) return true\n return title.toLowerCase().startsWith(keywords[0].toLowerCase())\n}\n\nexport const truncate = (text: string, maxLength: number) =>\n text.length > maxLength ? text.slice(0, maxLength) + '…' : text\n\nexport const hasExcessivePunctuation = (title: string): boolean => /[!@#$%^&*]{2,}/.test(title)\n\nexport const getMetaTitleValidationMessages = (\n title: string,\n keywords: string[],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n\n const minChar = 50\n const maxChar = 60\n const charCount = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'Meta Title is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Title is ${charCount} characters — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Title is ${charCount} characters — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else feedback.push({text: `Title length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in title — good job!'\n : 'Keywords defined but missing in title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word check\n if (startsWithStopWord(title))\n feedback.push({text: 'Title starts with a stop word — consider rephrasing.', color: 'orange'})\n\n // Punctuation check\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getMetaDescriptionValidationMessages = (\n description: string,\n keywords: string[],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n\n const minChar = 150\n const maxChar = 160\n const charCount = description?.length || 0\n\n if (!description?.trim()) {\n feedback.push({text: 'Meta description is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Description is ${charCount} chars — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Description is ${charCount} chars — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else\n feedback.push({text: `Description length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(description, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in description — good job!'\n : 'Keywords defined but missing in description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(description, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word / filler check\n if (startsWithStopWord(description))\n feedback.push({\n text: 'Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n // Punctuation / special characters\n if (hasExcessivePunctuation(description))\n feedback.push({\n text: 'Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getOgTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 40\n const max = 60\n const count = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'OG Title is empty. Add content for better social preview.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({text: `OG Title is ${count} chars — exceeds recommended ${max}.`, color: 'red'})\n else feedback.push({text: `OG Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG title — good job!'\n : 'Keywords defined but missing in OG title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG title — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(title))\n feedback.push({\n text: 'OG Title starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'OG Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getOgDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 90\n const max = 120\n const count = desc?.length || 0\n\n // Empty check\n if (!desc?.trim()) {\n feedback.push({\n text: 'OG Description is empty. Add content for better social preview.',\n color: 'red',\n })\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `OG Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `OG Description length (${count}) looks good.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG description — good job!'\n : 'Keywords defined but missing in OG description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(desc, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG description — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(desc))\n feedback.push({\n text: 'OG Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(desc))\n feedback.push({\n text: 'OG Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getTwitterTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 30\n const max = 70\n const count = title?.length || 0\n\n if (!title?.trim()) {\n feedback.push({text: 'Twitter Title is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `Twitter Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `Twitter Title is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `Twitter Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in Twitter title — good job!'\n : 'Keywords defined but missing in Twitter title.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(title))\n feedback.push({text: 'Twitter Title has excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getTwitterDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 50\n const max = 200\n const count = desc?.length || 0\n\n if (!desc?.trim()) {\n feedback.push({text: 'Twitter Description is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `Twitter Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `Twitter Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `Twitter Description length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in Twitter description — good job!'\n : 'Keywords defined but missing in Twitter description.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(desc))\n feedback.push({\n text: 'Twitter Description has excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n","import {StringInputProps, useFormValue, useClient, set} from 'sanity'\nimport React, {useEffect, useMemo} from 'react'\nimport {Stack, Text} from '@sanity/ui'\nimport {getMetaTitleValidationMessages} from '../../utils/seoUtils'\n\nconst MetaTitle = (props: StringInputProps) => {\n const client = useClient({apiVersion: '2024-05-05'})\n const {value, onChange, renderDefault, path} = props\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n // Fetch home page title if empty\n // useEffect(() => {\n // if (value) return\n // const fetchData = async () => {\n // const data = await client.fetch(\"*[_type=='homePage'][0]{'title':seo.title}\")\n // if (data?.title && !value) onChange(set(data.title))\n // }\n // fetchData()\n // }, [client, onChange, value])\n\n const feedbackItems = useMemo(\n () => getMetaTitleValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaTitle\n","import {StringInputProps, useFormValue, useClient, set} from 'sanity'\nimport React, {useEffect, useMemo} from 'react'\nimport {Stack, Text} from '@sanity/ui'\nimport {getMetaDescriptionValidationMessages} from '../../utils/seoUtils'\n\nconst MetaDescription = (props: StringInputProps) => {\n const client = useClient({apiVersion: '2024-05-05'})\n const {value, onChange, renderDefault, path} = props\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n // Fetch default meta description from home page if empty\n // useEffect(() => {\n // if (value) return\n // const fetchData = async () => {\n // const data = await client.fetch(\"*[_type=='homePage'][0]{'description':seo.description}\")\n // if (data?.description && !value) onChange(set(data.description))\n // }\n // fetchData()\n // }, [client, onChange, value])\n\n const feedbackItems = useMemo(\n () => getMetaDescriptionValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{width: 10, height: 10, borderRadius: '50%', backgroundColor: item.color}}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaDescription\n","import React from 'react'\nimport {Stack, Text, Box} from '@sanity/ui'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {truncate} from '../utils/seoUtils'\n\ninterface SeoPreviewProps {\n title?: string\n description?: string\n url?: string\n}\n\nconst SeoPreview = (props: StringInputProps) => {\n const {path} = props\n const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n console.log('SEO Preview Parent:', parent)\n\n const {\n title: title,\n description: description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n return (\n <Box\n padding={3}\n style={{\n maxWidth: 600,\n fontFamily: 'Arial, sans-serif',\n }}\n >\n <Stack space={3}>\n {/* URL */}\n <Text size={1} style={{color: '#006621', fontSize: 12, lineHeight: 1.3, marginBottom: 3}}>\n {url || 'https://www.example.com/page-url'}\n </Text>\n\n {/* Title */}\n <Text\n size={3}\n weight=\"bold\"\n style={{\n color: '#1a0dab',\n fontSize: 18,\n lineHeight: 1.4,\n marginBottom: 3,\n }}\n >\n {title ? truncate(title, 60) : 'Meta Title Preview'}\n </Text>\n\n {/* Description */}\n <Text\n size={2}\n style={{\n color: '#545454',\n fontSize: 14,\n lineHeight: 1.6,\n }}\n >\n {description ? truncate(description, 160) : 'Meta description will appear here…'}\n </Text>\n </Stack>\n </Box>\n )\n}\n\nexport default SeoPreview\n","// import {defineField, defineType} from 'sanity'\n// import OgTitle from '../../../components/openGraph/OgTitle'\n// import OgDescription from '../../../components/openGraph/OgDescription'\n\nimport {SeoField, SeoFieldsPluginConfig} from '../plugin'\n\n// export default defineType({\n// name: 'openGraph',\n// title: 'Open Graph Settings',\n// type: 'object',\n// fields: [\n// defineField({\n// name: 'title',\n// title: 'Open Graph Title',\n// type: 'string',\n// components: {\n// input: OgTitle, // Can also wrap with a string input + preview\n// },\n// }),\n// defineField({\n// name: 'description',\n// title: 'Open Graph Description',\n// type: 'text',\n// rows: 3,\n// components: {\n// input: OgDescription, // Can also wrap with a text area + preview\n// },\n// }),\n// defineField({\n// name: 'siteName',\n// title: 'Open Graph Site Name',\n// type: 'string',\n// description: 'The name of your website. This is often the site title.',\n// }),\n// defineField({\n// name: 'type',\n// title: 'Open Graph Type',\n// type: 'string',\n// options: {\n// list: [\n// {title: 'Website', value: 'website'},\n// {title: 'Article', value: 'article'},\n// {title: 'Profile', value: 'profile'},\n// {title: 'Book', value: 'book'},\n// {title: 'Music', value: 'music'},\n// {title: 'Video', value: 'video'},\n// {title: 'Product', value: 'product'},\n// ],\n// // layout: 'radio', // Display as radio buttons\n// },\n// initialValue: 'website',\n// description: 'Select the type of content for Open Graph.',\n// }),\n\n// // upload image or ask for url\n// defineField({\n// name: 'imageType',\n// title: 'Image Type',\n// type: 'string',\n// options: {\n// list: [\n// {title: 'Upload Image', value: 'upload'},\n// {title: 'Image URL', value: 'url'},\n// ],\n// },\n// initialValue: 'upload',\n// }),\n// defineField({\n// name: 'image',\n// title: 'Open Graph Image',\n// type: 'image',\n// options: {\n// hotspot: true,\n// },\n// hidden: ({parent}) => parent?.imageType !== 'upload',\n// description:\n// 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n// }),\n// defineField({\n// name: 'imageUrl',\n// title: 'Open Graph Image URL',\n// type: 'url',\n// hidden: ({parent}) => parent?.imageType !== 'url',\n// description:\n// 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',\n// }),\n// ],\n// })\n\nconst DEFAULT_FIELD_INFO = {\n title: {\n title: 'Meta Title',\n description:\n 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',\n },\n description: {\n title: 'Meta Description',\n description:\n 'Provide a concise summary of the page content. This description may be used by search engines in search results.',\n },\n metaImage: {\n title: 'Meta Image',\n description:\n 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',\n },\n keywords: {\n title: 'Keywords',\n description:\n 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',\n },\n canonicalUrl: {\n title: 'Canonical URL',\n description:\n 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',\n },\n robots: {\n title: 'Robots Settings',\n description: 'Configure how search engine crawlers should index and follow links on this page.',\n },\n metaAttributes: {\n title: 'Additional Meta Attributes',\n description:\n 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\n },\n}\n\nexport const getFieldInfo = (\n fieldName: string,\n fieldOverrides: SeoFieldsPluginConfig['fieldOverrides'],\n) => {\n const fieldInfo =\n (fieldOverrides && fieldOverrides[fieldName as keyof typeof fieldOverrides]) ||\n DEFAULT_FIELD_INFO[fieldName as keyof typeof DEFAULT_FIELD_INFO]\n return fieldInfo\n ? {title: fieldInfo.title, description: fieldInfo.description}\n : {title: '', description: ''}\n}\n","import {defineField, defineType} from 'sanity'\nimport MetaTitle from '../components/meta/MetaTitle'\nimport MetaDescription from '../components/meta/MetaDescription'\nimport SeoPreview from '../components/SeoPreview'\nimport {SeoFieldsPluginConfig} from '../plugin'\nimport {getFieldInfo} from '../utils/fieldsUtils'\n\nexport default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}) {\n return defineType({\n name: 'seoFields',\n title: 'SEO Fields',\n type: 'object',\n fields: [\n defineField({\n name: 'robots',\n title: 'Robots Settings',\n type: 'robots', // Use the separate robots type here\n }),\n // 👇 conditionally spread preview field\n ...(config.seoPreview\n ? []\n : [\n defineField({\n name: 'preview',\n title: 'SEO Preview',\n type: 'string',\n components: {input: SeoPreview},\n readOnly: true,\n }),\n ]),\n\n defineField({\n name: 'title',\n ...getFieldInfo('title', config.fieldOverrides),\n // title: 'Meta Title',\n type: 'string',\n // description:\n // 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',\n components: {\n input: MetaTitle,\n },\n // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),\n }),\n defineField({\n name: 'description',\n ...getFieldInfo('description', config.fieldOverrides),\n // title: 'Meta Description',\n // description:\n // 'Provide a concise summary of the page content. This description may be used by search engines in search results.',\n type: 'text',\n rows: 3,\n components: {\n input: MetaDescription,\n },\n // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),\n }),\n defineField({\n name: 'metaImage',\n ...getFieldInfo('metaImage', config.fieldOverrides),\n // title: 'Meta Image',\n // description:\n // 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',\n type: 'image',\n options: {\n hotspot: true,\n },\n }),\n defineField({\n name: 'metaAttributes',\n title: 'Additional Meta Attributes',\n type: 'array',\n of: [{type: 'metaAttribute'}],\n description:\n 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\n }),\n defineField({\n name: 'keywords',\n ...getFieldInfo('keywords', config.fieldOverrides),\n title: 'Keywords',\n type: 'array',\n of: [{type: 'string'}],\n description:\n 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',\n }),\n defineField({\n name: 'canonicalUrl',\n ...getFieldInfo('canonicalUrl', config.fieldOverrides),\n title: 'Canonical URL',\n type: 'url',\n description:\n 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',\n }),\n defineField({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'openGraph',\n }),\n defineField({\n name: 'twitter',\n title: 'Twitter Card Settings',\n type: 'twitter',\n }),\n ],\n })\n}\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'metaAttribute',\n title: 'Meta Attribute',\n type: 'object',\n fields: [\n defineField({\n name: 'key',\n title: 'Attribute Name',\n type: 'string',\n }),\n defineField({\n name: 'type',\n title: 'Attribute Value Type',\n type: 'string',\n options: {\n list: [\n {title: 'String', value: 'string'},\n {title: 'Image', value: 'image'},\n ],\n },\n initialValue: 'string',\n }),\n defineField({\n name: 'value',\n title: 'Attribute Value',\n type: 'string',\n hidden: ({parent}) => parent?.type === 'image',\n }),\n defineField({\n name: 'image',\n title: 'Attribute Image Value',\n type: 'image',\n hidden: ({parent}) => parent?.type === 'string',\n }),\n ],\n preview: {\n select: {\n attributeName: 'key',\n attributeValueString: 'value',\n attributeValueImage: 'image',\n },\n prepare({\n attributeName,\n attributeValueString,\n attributeValueImage,\n }: {\n attributeName: string\n attributeValueString: string\n attributeValueImage: any\n }) {\n return {\n title: attributeName || 'No Attribute Name',\n subtitle: attributeValueString\n ? `Value: ${attributeValueString}`\n : attributeValueImage\n ? 'Value: [Image]'\n : 'No Attribute Value',\n media: attributeValueImage,\n }\n },\n },\n})\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text, Box} from '@sanity/ui'\nimport { getOgTitleValidation } from '../../utils/seoUtils'\n\nconst OgTitle: React.FC<StringInputProps> = (props) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getOgTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default OgTitle\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getOgDescriptionValidation} from '../../utils/seoUtils'\n\nconst OgDescription = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getOgDescriptionValidation(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n {/* Validation */}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default OgDescription\n","import {defineField, defineType} from 'sanity'\nimport OgTitle from '../../../components/openGraph/OgTitle'\nimport OgDescription from '../../../components/openGraph/OgDescription'\n\nexport default defineType({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'title',\n title: 'Open Graph Title',\n type: 'string',\n components: {\n input: OgTitle, // Can also wrap with a string input + preview\n },\n }),\n defineField({\n name: 'description',\n title: 'Open Graph Description',\n type: 'text',\n rows: 3,\n components: {\n input: OgDescription, // Can also wrap with a text area + preview\n },\n }),\n defineField({\n name: 'siteName',\n title: 'Open Graph Site Name',\n type: 'string',\n description: 'The name of your website. This is often the site title.',\n }),\n defineField({\n name: 'type',\n title: 'Open Graph Type',\n type: 'string',\n options: {\n list: [\n {title: 'Website', value: 'website'},\n {title: 'Article', value: 'article'},\n {title: 'Profile', value: 'profile'},\n {title: 'Book', value: 'book'},\n {title: 'Music', value: 'music'},\n {title: 'Video', value: 'video'},\n {title: 'Product', value: 'product'},\n ],\n // layout: 'radio', // Display as radio buttons\n },\n initialValue: 'website',\n description: 'Select the type of content for Open Graph.',\n }),\n\n // upload image or ask for url\n defineField({\n name: 'imageType',\n title: 'Image Type',\n type: 'string',\n options: {\n list: [\n {title: 'Upload Image', value: 'upload'},\n {title: 'Image URL', value: 'url'},\n ],\n },\n initialValue: 'upload',\n }),\n defineField({\n name: 'image',\n title: 'Open Graph Image',\n type: 'image',\n options: {\n hotspot: true,\n },\n hidden: ({parent}) => parent?.imageType !== 'upload',\n description:\n 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n }),\n defineField({\n name: 'imageUrl',\n title: 'Open Graph Image URL',\n type: 'url',\n hidden: ({parent}) => parent?.imageType !== 'url',\n description:\n 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',\n }),\n ],\n})\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getTwitterTitleValidation} from '../../utils/seoUtils'\n\nconst TwitterTitle = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getTwitterTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default TwitterTitle\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getTwitterDescriptionValidation} from '../../utils/seoUtils'\n\nconst TwitterDescription = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getTwitterDescriptionValidation(value || '', keywords,isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default TwitterDescription\n","import {defineField, defineType} from 'sanity'\nimport TwitterTitle from '../../../components/twitter/twitterTitle'\nimport TwitterDescription from '../../../components/twitter/twitterDescription'\n\nexport default defineType({\n name: 'twitter',\n title: 'Twitter',\n type: 'object',\n fields: [\n defineField({\n name: 'card',\n title: 'Card Type',\n type: 'string',\n options: {\n list: [\n {title: 'Summary', value: 'summary'},\n {title: 'Summary with Large Image', value: 'summary_large_image'},\n {title: 'App', value: 'app'},\n {title: 'Player', value: 'player'},\n ],\n },\n initialValue: 'summary_large_image', // good default\n }),\n defineField({\n name: 'site',\n title: 'Site Twitter Handle',\n type: 'string',\n description: 'The Twitter handle of the website (e.g., @example)',\n }),\n defineField({\n name: 'title',\n title: 'Twitter Title',\n type: 'string',\n description: 'The title of the content as it should appear on Twitter.',\n components: {\n input: TwitterTitle,\n },\n }),\n defineField({\n name: 'description',\n title: 'Twitter Description',\n type: 'text',\n rows: 3,\n description: 'A brief description of the content for Twitter.',\n components: {\n input: TwitterDescription,\n },\n }),\n defineField({\n name: 'image',\n title: 'Twitter Image',\n type: 'image',\n description:\n 'An image URL which should be at least 120x120px for \"summary\" card and 280x150px for \"summary_large_image\" card.',\n options: {\n hotspot: true,\n },\n fields: [\n defineField({\n name: 'alt',\n title: 'Image Alt Text',\n type: 'string',\n description: 'Short alt text describing the image',\n }),\n ],\n }),\n ],\n})\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'robots',\n title: 'Robots Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'noIndex',\n title: 'No Index',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from indexing this page. The page will not appear in search results.',\n }),\n defineField({\n name: 'noFollow',\n title: 'No Follow',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from following links on this page. Links will not pass SEO value.',\n }),\n ],\n description: 'Select how search engines should index and follow links on this page.',\n})\n","import { defineType } from \"sanity\";\n\nexport default defineType({\n name: \"metaTag\",\n title: \"Meta Tag\",\n type: \"object\",\n fields: [\n {\n name: \"metaAttributes\",\n title: \"Meta Attributes\",\n type: \"array\",\n of: [{ type: \"metaAttribute\" }],\n description: \"Add custom meta attributes to the head of the document for additional SEO and social media integration.\",\n },\n ],\n})","import seoFields from '..'\nimport metaAttribute from './metaAttribute'\nimport openGraph from './openGraph'\nimport twitter from './twitter'\nimport robots from './robots'\nimport metaTag from './metaTag'\nimport {SeoFieldsPluginConfig} from '../../plugin'\n\nexport default function types(config: SeoFieldsPluginConfig = {}) {\n return [\n seoFields(config), // pass config here\n openGraph,\n twitter,\n metaAttribute,\n metaTag,\n robots,\n ]\n}\n","// plugin.ts\nimport {definePlugin} from 'sanity'\nimport types from './schemas/types'\n\nexport interface SeoField {\n title?: string\n description?: string\n}\n\nexport type SeoFieldKeys = 'title' | 'description' | 'canonicalUrl' | 'metaImage' | 'keywords'\n\nexport interface SeoFieldsPluginConfig {\n seoPreview?: boolean\n fieldOverrides?: {\n [key in SeoFieldKeys]?: SeoField\n }\n}\n\nconst seofields = definePlugin<SeoFieldsPluginConfig | void>((config = {}) => {\n return {\n name: 'sanity-plugin-seofields',\n schema: {\n types: types(config as SeoFieldsPluginConfig), // pass config down to schemas\n },\n }\n})\n\nexport default seofields\n","// TypeScript interfaces for SEO Fields Plugin\n\n// Base Sanity types\nexport interface SanityImage {\n _type: 'image'\n asset: {\n _ref: string\n _type: 'reference'\n }\n hotspot?: {\n x: number\n y: number\n height: number\n width: number\n }\n crop?: {\n top: number\n bottom: number\n left: number\n right: number\n }\n alt?: string\n}\n\nexport interface SanityImageWithAlt extends SanityImage {\n alt: string\n}\n\n// Robots settings\nexport interface RobotsSettings {\n noIndex?: boolean\n noFollow?: boolean\n}\n\n// Meta Attribute\nexport interface MetaAttribute {\n _type: 'metaAttribute'\n key: string\n type: 'string' | 'image'\n value?: string\n image?: SanityImage\n}\n\n// Open Graph settings\nexport interface OpenGraphSettings {\n _type: 'openGraph'\n title?: string\n description?: string\n siteName?: string\n type?: 'website' | 'article' | 'profile' | 'book' | 'music' | 'video' | 'product'\n imageType?: 'upload' | 'url'\n image?: SanityImage\n imageUrl?: string\n}\n\n// Twitter Card settings\nexport interface TwitterCardSettings {\n _type: 'twitter'\n card?: 'summary' | 'summary_large_image' | 'app' | 'player'\n site?: string\n title?: string\n description?: string\n image?: SanityImageWithAlt\n}\n\n// Main SEO Fields interface\nexport interface SeoFields {\n _type: 'seoFields'\n robots?: RobotsSettings\n preview?: string\n title?: string\n description?: string\n metaImage?: SanityImage\n keywords?: string[]\n canonicalUrl?: string\n openGraph?: OpenGraphSettings\n twitter?: TwitterCardSettings\n}\n\n// Type guards\nexport const isSeoFields = (obj: any): obj is SeoFields => {\n return obj && obj._type === 'seoFields'\n}\n\nexport const isOpenGraphSettings = (obj: any): obj is OpenGraphSettings => {\n return obj && obj._type === 'openGraph'\n}\n\nexport const isTwitterCardSettings = (obj: any): obj is TwitterCardSettings => {\n return obj && obj._type === 'twitter'\n}\n\nexport const isMetaAttribute = (obj: any): obj is MetaAttribute => {\n return obj && obj._type === 'metaAttribute'\n}\n\n// Utility types for form validation\nexport interface SeoValidationRules {\n title: {\n maxLength: number\n warningLength: number\n }\n description: {\n maxLength: number\n warningLength: number\n }\n openGraphTitle: {\n maxLength: number\n }\n openGraphDescription: {\n maxLength: number\n }\n twitterTitle: {\n maxLength: number\n }\n twitterDescription: {\n maxLength: number\n }\n}\n\nexport const defaultSeoValidationRules: SeoValidationRules = {\n title: {\n maxLength: 70,\n warningLength: 60,\n },\n description: {\n maxLength: 160,\n warningLength: 150,\n },\n openGraphTitle: {\n maxLength: 95,\n },\n openGraphDescription: {\n maxLength: 200,\n },\n twitterTitle: {\n maxLength: 70,\n },\n twitterDescription: {\n maxLength: 200,\n },\n}\n\n// All types are already exported above with individual export statements\n"],"names":["useClient","useFormValue","useMemo","jsxs","Stack","jsx","Text","Box","defineType","defineField","seoFields","definePlugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/utils/seoUtils.ts","../src/components/meta/MetaTitle.tsx","../src/components/meta/MetaDescription.tsx","../src/components/SeoPreview.tsx","../src/utils/fieldsUtils.ts","../src/schemas/index.ts","../src/schemas/types/metaAttribute/index.ts","../src/components/openGraph/OgTitle.tsx","../src/components/openGraph/OgDescription.tsx","../src/schemas/types/openGraph/index.ts","../src/components/twitter/twitterTitle.tsx","../src/components/twitter/twitterDescription.tsx","../src/schemas/types/twitter/index.ts","../src/schemas/types/robots/index.ts","../src/schemas/types/metaTag/index.ts","../src/schemas/types/index.ts","../src/plugin.ts","../src/types.ts"],"sourcesContent":["import {PathSegment, useFormValue} from 'sanity'\n\nexport const stopWords = ['the', 'a', 'an', 'and', 'or', 'but']\n\nexport const hasMatchingKeyword = (title: string, keywordList: string[]): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()))\n}\n\nexport const hasKeywordOveruse = (\n title: string,\n keywordList: string[],\n maxOccurrences = 3,\n): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => {\n if (!keyword) return false\n const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), 'g'))\n return matches ? matches.length > maxOccurrences : false\n })\n}\n\nexport const startsWithStopWord = (title: string): boolean => {\n if (!title) return false\n const firstWord = title.trim().split(' ')[0].toLowerCase()\n return stopWords.includes(firstWord)\n}\n\nexport const primaryKeywordAtStart = (title: string, keywords: string[]): boolean => {\n if (!title || keywords.length === 0) return true\n return title.toLowerCase().startsWith(keywords[0].toLowerCase())\n}\n\nexport const truncate = (text: string, maxLength: number) =>\n text.length > maxLength ? text.slice(0, maxLength) + '…' : text\n\nexport const hasExcessivePunctuation = (title: string): boolean => /[!@#$%^&*]{2,}/.test(title)\n\nexport const getMetaTitleValidationMessages = (\n title: string,\n keywords: string[],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n\n const minChar = 50\n const maxChar = 60\n const charCount = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'Meta Title is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Title is ${charCount} characters — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Title is ${charCount} characters — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else feedback.push({text: `Title length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in title — good job!'\n : 'Keywords defined but missing in title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word check\n if (startsWithStopWord(title))\n feedback.push({text: 'Title starts with a stop word — consider rephrasing.', color: 'orange'})\n\n // Punctuation check\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getMetaDescriptionValidationMessages = (\n description: string,\n keywords: string[],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n\n const minChar = 150\n const maxChar = 160\n const charCount = description?.length || 0\n\n if (!description?.trim()) {\n feedback.push({text: 'Meta description is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Description is ${charCount} chars — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Description is ${charCount} chars — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else\n feedback.push({text: `Description length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(description, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in description — good job!'\n : 'Keywords defined but missing in description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(description, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word / filler check\n if (startsWithStopWord(description))\n feedback.push({\n text: 'Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n // Punctuation / special characters\n if (hasExcessivePunctuation(description))\n feedback.push({\n text: 'Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getOgTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 40\n const max = 60\n const count = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'OG Title is empty. Add content for better social preview.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({text: `OG Title is ${count} chars — exceeds recommended ${max}.`, color: 'red'})\n else feedback.push({text: `OG Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG title — good job!'\n : 'Keywords defined but missing in OG title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG title — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(title))\n feedback.push({\n text: 'OG Title starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'OG Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getOgDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 90\n const max = 120\n const count = desc?.length || 0\n\n // Empty check\n if (!desc?.trim()) {\n feedback.push({\n text: 'OG Description is empty. Add content for better social preview.',\n color: 'red',\n })\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `OG Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `OG Description length (${count}) looks good.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG description — good job!'\n : 'Keywords defined but missing in OG description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(desc, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG description — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(desc))\n feedback.push({\n text: 'OG Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(desc))\n feedback.push({\n text: 'OG Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getTwitterTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 30\n const max = 70\n const count = title?.length || 0\n\n if (!title?.trim()) {\n feedback.push({text: 'Twitter Title is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `Twitter Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `Twitter Title is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `Twitter Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in Twitter title — good job!'\n : 'Keywords defined but missing in Twitter title.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(title))\n feedback.push({text: 'Twitter Title has excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getTwitterDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 50\n const max = 200\n const count = desc?.length || 0\n\n if (!desc?.trim()) {\n feedback.push({text: 'Twitter Description is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `Twitter Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `Twitter Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `Twitter Description length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in Twitter description — good job!'\n : 'Keywords defined but missing in Twitter description.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(desc))\n feedback.push({\n text: 'Twitter Description has excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n","import {StringInputProps, useFormValue, useClient, set} from 'sanity'\nimport React, {useEffect, useMemo} from 'react'\nimport {Stack, Text} from '@sanity/ui'\nimport {getMetaTitleValidationMessages} from '../../utils/seoUtils'\n\nconst MetaTitle = (props: StringInputProps) => {\n const client = useClient({apiVersion: '2024-05-05'})\n const {value, onChange, renderDefault, path} = props\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n // Fetch home page title if empty\n // useEffect(() => {\n // if (value) return\n // const fetchData = async () => {\n // const data = await client.fetch(\"*[_type=='homePage'][0]{'title':seo.title}\")\n // if (data?.title && !value) onChange(set(data.title))\n // }\n // fetchData()\n // }, [client, onChange, value])\n\n const feedbackItems = useMemo(\n () => getMetaTitleValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaTitle\n","import {StringInputProps, useFormValue, useClient, set} from 'sanity'\nimport React, {useEffect, useMemo} from 'react'\nimport {Stack, Text} from '@sanity/ui'\nimport {getMetaDescriptionValidationMessages} from '../../utils/seoUtils'\n\nconst MetaDescription = (props: StringInputProps) => {\n const client = useClient({apiVersion: '2024-05-05'})\n const {value, onChange, renderDefault, path} = props\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n // Fetch default meta description from home page if empty\n // useEffect(() => {\n // if (value) return\n // const fetchData = async () => {\n // const data = await client.fetch(\"*[_type=='homePage'][0]{'description':seo.description}\")\n // if (data?.description && !value) onChange(set(data.description))\n // }\n // fetchData()\n // }, [client, onChange, value])\n\n const feedbackItems = useMemo(\n () => getMetaDescriptionValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{width: 10, height: 10, borderRadius: '50%', backgroundColor: item.color}}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaDescription\n","import React from 'react'\nimport {Stack, Text, Box} from '@sanity/ui'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {truncate} from '../utils/seoUtils'\n\ninterface SeoPreviewProps {\n title?: string\n description?: string\n url?: string\n}\n\nconst SeoPreview = (props: StringInputProps) => {\n const {path} = props\n const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n console.log('SEO Preview Parent:', parent)\n\n const {\n title: title,\n description: description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n return (\n <Box\n padding={3}\n style={{\n maxWidth: 600,\n fontFamily: 'Arial, sans-serif',\n }}\n >\n <Stack space={3}>\n {/* URL */}\n <Text size={1} style={{color: '#006621', fontSize: 12, lineHeight: 1.3, marginBottom: 3}}>\n {url || 'https://www.example.com/page-url'}\n </Text>\n\n {/* Title */}\n <Text\n size={3}\n weight=\"bold\"\n style={{\n color: '#1a0dab',\n fontSize: 18,\n lineHeight: 1.4,\n marginBottom: 3,\n }}\n >\n {title ? truncate(title, 60) : 'Meta Title Preview'}\n </Text>\n\n {/* Description */}\n <Text\n size={2}\n style={{\n color: '#545454',\n fontSize: 14,\n lineHeight: 1.6,\n }}\n >\n {description ? truncate(description, 160) : 'Meta description will appear here…'}\n </Text>\n </Stack>\n </Box>\n )\n}\n\nexport default SeoPreview\n","// import {defineField, defineType} from 'sanity'\n// import OgTitle from '../../../components/openGraph/OgTitle'\n// import OgDescription from '../../../components/openGraph/OgDescription'\n\nimport {SeoFieldsPluginConfig} from '../plugin'\n\n// export default defineType({\n// name: 'openGraph',\n// title: 'Open Graph Settings',\n// type: 'object',\n// fields: [\n// defineField({\n// name: 'title',\n// title: 'Open Graph Title',\n// type: 'string',\n// components: {\n// input: OgTitle, // Can also wrap with a string input + preview\n// },\n// }),\n// defineField({\n// name: 'description',\n// title: 'Open Graph Description',\n// type: 'text',\n// rows: 3,\n// components: {\n// input: OgDescription, // Can also wrap with a text area + preview\n// },\n// }),\n// defineField({\n// name: 'siteName',\n// title: 'Open Graph Site Name',\n// type: 'string',\n// description: 'The name of your website. This is often the site title.',\n// }),\n// defineField({\n// name: 'type',\n// title: 'Open Graph Type',\n// type: 'string',\n// options: {\n// list: [\n// {title: 'Website', value: 'website'},\n// {title: 'Article', value: 'article'},\n// {title: 'Profile', value: 'profile'},\n// {title: 'Book', value: 'book'},\n// {title: 'Music', value: 'music'},\n// {title: 'Video', value: 'video'},\n// {title: 'Product', value: 'product'},\n// ],\n// // layout: 'radio', // Display as radio buttons\n// },\n// initialValue: 'website',\n// description: 'Select the type of content for Open Graph.',\n// }),\n\n// // upload image or ask for url\n// defineField({\n// name: 'imageType',\n// title: 'Image Type',\n// type: 'string',\n// options: {\n// list: [\n// {title: 'Upload Image', value: 'upload'},\n// {title: 'Image URL', value: 'url'},\n// ],\n// },\n// initialValue: 'upload',\n// }),\n// defineField({\n// name: 'image',\n// title: 'Open Graph Image',\n// type: 'image',\n// options: {\n// hotspot: true,\n// },\n// hidden: ({parent}) => parent?.imageType !== 'upload',\n// description:\n// 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n// }),\n// defineField({\n// name: 'imageUrl',\n// title: 'Open Graph Image URL',\n// type: 'url',\n// hidden: ({parent}) => parent?.imageType !== 'url',\n// description:\n// 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',\n// }),\n// ],\n// })\n\nconst DEFAULT_FIELD_INFO = {\n title: {\n title: 'Meta Title',\n description:\n 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',\n },\n description: {\n title: 'Meta Description',\n description:\n 'Provide a concise summary of the page content. This description may be used by search engines in search results.',\n },\n metaImage: {\n title: 'Meta Image',\n description:\n 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',\n },\n keywords: {\n title: 'Keywords',\n description:\n 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',\n },\n canonicalUrl: {\n title: 'Canonical URL',\n description:\n 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',\n },\n robots: {\n title: 'Robots Settings',\n description: 'Configure how search engine crawlers should index and follow links on this page.',\n },\n metaAttributes: {\n title: 'Additional Meta Attributes',\n description:\n 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\n },\n}\n\nexport const getFieldInfo = (\n fieldName: string,\n fieldOverrides: SeoFieldsPluginConfig['fieldOverrides'],\n) => {\n const fieldInfo =\n (fieldOverrides && fieldOverrides[fieldName as keyof typeof fieldOverrides]) ||\n DEFAULT_FIELD_INFO[fieldName as keyof typeof DEFAULT_FIELD_INFO]\n return fieldInfo\n ? {title: fieldInfo.title, description: fieldInfo.description}\n : {title: '', description: ''}\n}\n","import {defineField, defineType} from 'sanity'\nimport MetaTitle from '../components/meta/MetaTitle'\nimport MetaDescription from '../components/meta/MetaDescription'\nimport SeoPreview from '../components/SeoPreview'\nimport {SeoFieldsPluginConfig} from '../plugin'\nimport {getFieldInfo} from '../utils/fieldsUtils'\n\nexport default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}) {\n return defineType({\n name: 'seoFields',\n title: 'SEO Fields',\n type: 'object',\n fields: [\n defineField({\n name: 'robots',\n title: 'Robots Settings',\n type: 'robots', // Use the separate robots type here\n }),\n // 👇 conditionally spread preview field\n ...(config.seoPreview\n ? []\n : [\n defineField({\n name: 'preview',\n title: 'SEO Preview',\n type: 'string',\n components: {input: SeoPreview},\n readOnly: true,\n }),\n ]),\n\n defineField({\n name: 'title',\n ...getFieldInfo('title', config.fieldOverrides),\n // title: 'Meta Title',\n type: 'string',\n // description:\n // 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',\n components: {\n input: MetaTitle,\n },\n // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),\n }),\n defineField({\n name: 'description',\n ...getFieldInfo('description', config.fieldOverrides),\n // title: 'Meta Description',\n // description:\n // 'Provide a concise summary of the page content. This description may be used by search engines in search results.',\n type: 'text',\n rows: 3,\n components: {\n input: MetaDescription,\n },\n // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),\n }),\n defineField({\n name: 'metaImage',\n ...getFieldInfo('metaImage', config.fieldOverrides),\n // title: 'Meta Image',\n // description:\n // 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',\n type: 'image',\n options: {\n hotspot: true,\n },\n }),\n defineField({\n name: 'metaAttributes',\n // title: 'Additional Meta Attributes',\n ...getFieldInfo('metaAttributes', config.fieldOverrides),\n type: 'array',\n of: [{type: 'metaAttribute'}],\n // description:\n // 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\n }),\n defineField({\n name: 'keywords',\n ...getFieldInfo('keywords', config.fieldOverrides),\n title: 'Keywords',\n type: 'array',\n of: [{type: 'string'}],\n description:\n 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',\n }),\n defineField({\n name: 'canonicalUrl',\n ...getFieldInfo('canonicalUrl', config.fieldOverrides),\n title: 'Canonical URL',\n type: 'url',\n description:\n 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',\n }),\n defineField({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'openGraph',\n }),\n defineField({\n name: 'twitter',\n title: 'Twitter Card Settings',\n type: 'twitter',\n }),\n ],\n })\n}\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'metaAttribute',\n title: 'Meta Attribute',\n type: 'object',\n fields: [\n defineField({\n name: 'key',\n title: 'Attribute Name',\n type: 'string',\n }),\n defineField({\n name: 'type',\n title: 'Attribute Value Type',\n type: 'string',\n options: {\n list: [\n {title: 'String', value: 'string'},\n {title: 'Image', value: 'image'},\n ],\n },\n initialValue: 'string',\n }),\n defineField({\n name: 'value',\n title: 'Attribute Value',\n type: 'string',\n hidden: ({parent}) => parent?.type === 'image',\n }),\n defineField({\n name: 'image',\n title: 'Attribute Image Value',\n type: 'image',\n hidden: ({parent}) => parent?.type === 'string',\n }),\n ],\n preview: {\n select: {\n attributeName: 'key',\n attributeValueString: 'value',\n attributeValueImage: 'image',\n },\n prepare({\n attributeName,\n attributeValueString,\n attributeValueImage,\n }: {\n attributeName: string\n attributeValueString: string\n attributeValueImage: any\n }) {\n return {\n title: attributeName || 'No Attribute Name',\n subtitle: attributeValueString\n ? `Value: ${attributeValueString}`\n : attributeValueImage\n ? 'Value: [Image]'\n : 'No Attribute Value',\n media: attributeValueImage,\n }\n },\n },\n})\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text, Box} from '@sanity/ui'\nimport { getOgTitleValidation } from '../../utils/seoUtils'\n\nconst OgTitle: React.FC<StringInputProps> = (props) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getOgTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default OgTitle\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getOgDescriptionValidation} from '../../utils/seoUtils'\n\nconst OgDescription = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getOgDescriptionValidation(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n {/* Validation */}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default OgDescription\n","import {defineField, defineType} from 'sanity'\nimport OgTitle from '../../../components/openGraph/OgTitle'\nimport OgDescription from '../../../components/openGraph/OgDescription'\n\nexport default defineType({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'title',\n title: 'Open Graph Title',\n type: 'string',\n components: {\n input: OgTitle, // Can also wrap with a string input + preview\n },\n }),\n defineField({\n name: 'description',\n title: 'Open Graph Description',\n type: 'text',\n rows: 3,\n components: {\n input: OgDescription, // Can also wrap with a text area + preview\n },\n }),\n defineField({\n name: 'siteName',\n title: 'Open Graph Site Name',\n type: 'string',\n description: 'The name of your website. This is often the site title.',\n }),\n defineField({\n name: 'type',\n title: 'Open Graph Type',\n type: 'string',\n options: {\n list: [\n {title: 'Website', value: 'website'},\n {title: 'Article', value: 'article'},\n {title: 'Profile', value: 'profile'},\n {title: 'Book', value: 'book'},\n {title: 'Music', value: 'music'},\n {title: 'Video', value: 'video'},\n {title: 'Product', value: 'product'},\n ],\n // layout: 'radio', // Display as radio buttons\n },\n initialValue: 'website',\n description: 'Select the type of content for Open Graph.',\n }),\n\n // upload image or ask for url\n defineField({\n name: 'imageType',\n title: 'Image Type',\n type: 'string',\n options: {\n list: [\n {title: 'Upload Image', value: 'upload'},\n {title: 'Image URL', value: 'url'},\n ],\n },\n initialValue: 'upload',\n }),\n defineField({\n name: 'image',\n title: 'Open Graph Image',\n type: 'image',\n options: {\n hotspot: true,\n },\n hidden: ({parent}) => parent?.imageType !== 'upload',\n description:\n 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n }),\n defineField({\n name: 'imageUrl',\n title: 'Open Graph Image URL',\n type: 'url',\n hidden: ({parent}) => parent?.imageType !== 'url',\n description:\n 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',\n }),\n ],\n})\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getTwitterTitleValidation} from '../../utils/seoUtils'\n\nconst TwitterTitle = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getTwitterTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default TwitterTitle\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getTwitterDescriptionValidation} from '../../utils/seoUtils'\n\nconst TwitterDescription = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getTwitterDescriptionValidation(value || '', keywords,isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default TwitterDescription\n","import {defineField, defineType} from 'sanity'\nimport TwitterTitle from '../../../components/twitter/twitterTitle'\nimport TwitterDescription from '../../../components/twitter/twitterDescription'\n\nexport default defineType({\n name: 'twitter',\n title: 'Twitter',\n type: 'object',\n fields: [\n defineField({\n name: 'card',\n title: 'Card Type',\n type: 'string',\n options: {\n list: [\n {title: 'Summary', value: 'summary'},\n {title: 'Summary with Large Image', value: 'summary_large_image'},\n {title: 'App', value: 'app'},\n {title: 'Player', value: 'player'},\n ],\n },\n initialValue: 'summary_large_image', // good default\n }),\n defineField({\n name: 'site',\n title: 'Site Twitter Handle',\n type: 'string',\n description: 'The Twitter handle of the website (e.g., @example)',\n }),\n defineField({\n name: 'title',\n title: 'Twitter Title',\n type: 'string',\n description: 'The title of the content as it should appear on Twitter.',\n components: {\n input: TwitterTitle,\n },\n }),\n defineField({\n name: 'description',\n title: 'Twitter Description',\n type: 'text',\n rows: 3,\n description: 'A brief description of the content for Twitter.',\n components: {\n input: TwitterDescription,\n },\n }),\n defineField({\n name: 'image',\n title: 'Twitter Image',\n type: 'image',\n description:\n 'An image URL which should be at least 120x120px for \"summary\" card and 280x150px for \"summary_large_image\" card.',\n options: {\n hotspot: true,\n },\n fields: [\n defineField({\n name: 'alt',\n title: 'Image Alt Text',\n type: 'string',\n description: 'Short alt text describing the image',\n }),\n ],\n }),\n ],\n})\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'robots',\n title: 'Robots Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'noIndex',\n title: 'No Index',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from indexing this page. The page will not appear in search results.',\n }),\n defineField({\n name: 'noFollow',\n title: 'No Follow',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from following links on this page. Links will not pass SEO value.',\n }),\n ],\n description: 'Select how search engines should index and follow links on this page.',\n})\n","import { defineType } from \"sanity\";\n\nexport default defineType({\n name: \"metaTag\",\n title: \"Meta Tag\",\n type: \"object\",\n fields: [\n {\n name: \"metaAttributes\",\n title: \"Meta Attributes\",\n type: \"array\",\n of: [{ type: \"metaAttribute\" }],\n description: \"Add custom meta attributes to the head of the document for additional SEO and social media integration.\",\n },\n ],\n})","import seoFields from '..'\nimport metaAttribute from './metaAttribute'\nimport openGraph from './openGraph'\nimport twitter from './twitter'\nimport robots from './robots'\nimport metaTag from './metaTag'\nimport {SeoFieldsPluginConfig} from '../../plugin'\n\nexport default function types(config: SeoFieldsPluginConfig = {}) {\n return [\n seoFields(config), // pass config here\n openGraph,\n twitter,\n metaAttribute,\n metaTag,\n robots,\n ]\n}\n","// plugin.ts\nimport {definePlugin} from 'sanity'\nimport types from './schemas/types'\n\nexport interface SeoFieldConfig {\n title?: string\n description?: string\n}\n\nexport type SeoFieldKeys =\n | 'title'\n | 'description'\n | 'canonicalUrl'\n | 'metaImage'\n | 'keywords'\n | 'metaAttributes'\n\nexport interface SeoFieldsPluginConfig {\n seoPreview?: boolean\n fieldOverrides?: {\n [key in SeoFieldKeys]?: SeoFieldConfig\n }\n}\n\nconst seofields = definePlugin<SeoFieldsPluginConfig | void>((config = {}) => {\n return {\n name: 'sanity-plugin-seofields',\n schema: {\n types: types(config as SeoFieldsPluginConfig), // pass config down to schemas\n },\n }\n})\n\nexport default seofields\n","// TypeScript interfaces for SEO Fields Plugin\n\n// Base Sanity types\nexport interface SanityImage {\n _type: 'image'\n asset: {\n _ref: string\n _type: 'reference'\n }\n hotspot?: {\n x: number\n y: number\n height: number\n width: number\n }\n crop?: {\n top: number\n bottom: number\n left: number\n right: number\n }\n alt?: string\n}\n\nexport interface SanityImageWithAlt extends SanityImage {\n alt: string\n}\n\n// Robots settings\nexport interface RobotsSettings {\n noIndex?: boolean\n noFollow?: boolean\n}\n\n// Meta Attribute\nexport interface MetaAttribute {\n _type: 'metaAttribute'\n key: string\n type: 'string' | 'image'\n value?: string\n image?: SanityImage\n}\n\n// Open Graph settings\nexport interface OpenGraphSettings {\n _type: 'openGraph'\n title?: string\n description?: string\n siteName?: string\n type?: 'website' | 'article' | 'profile' | 'book' | 'music' | 'video' | 'product'\n imageType?: 'upload' | 'url'\n image?: SanityImage\n imageUrl?: string\n}\n\n// Twitter Card settings\nexport interface TwitterCardSettings {\n _type: 'twitter'\n card?: 'summary' | 'summary_large_image' | 'app' | 'player'\n site?: string\n title?: string\n description?: string\n image?: SanityImageWithAlt\n}\n\n// Main SEO Fields interface\nexport interface SeoFields {\n _type: 'seoFields'\n robots?: RobotsSettings\n preview?: string\n title?: string\n description?: string\n metaImage?: SanityImage\n keywords?: string[]\n canonicalUrl?: string\n openGraph?: OpenGraphSettings\n twitter?: TwitterCardSettings\n}\n\n// Type guards\nexport const isSeoFields = (obj: any): obj is SeoFields => {\n return obj && obj._type === 'seoFields'\n}\n\nexport const isOpenGraphSettings = (obj: any): obj is OpenGraphSettings => {\n return obj && obj._type === 'openGraph'\n}\n\nexport const isTwitterCardSettings = (obj: any): obj is TwitterCardSettings => {\n return obj && obj._type === 'twitter'\n}\n\nexport const isMetaAttribute = (obj: any): obj is MetaAttribute => {\n return obj && obj._type === 'metaAttribute'\n}\n\n// Utility types for form validation\nexport interface SeoValidationRules {\n title: {\n maxLength: number\n warningLength: number\n }\n description: {\n maxLength: number\n warningLength: number\n }\n openGraphTitle: {\n maxLength: number\n }\n openGraphDescription: {\n maxLength: number\n }\n twitterTitle: {\n maxLength: number\n }\n twitterDescription: {\n maxLength: number\n }\n}\n\nexport const defaultSeoValidationRules: SeoValidationRules = {\n title: {\n maxLength: 70,\n warningLength: 60,\n },\n description: {\n maxLength: 160,\n warningLength: 150,\n },\n openGraphTitle: {\n maxLength: 95,\n },\n openGraphDescription: {\n maxLength: 200,\n },\n twitterTitle: {\n maxLength: 70,\n },\n twitterDescription: {\n maxLength: 200,\n },\n}\n\n// All types are already exported above with individual export statements\n"],"names":["useClient","useFormValue","useMemo","jsxs","Stack","jsx","Text","Box","defineType","defineField","seoFields","definePlugin"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -371,10 +371,12 @@ function seoFieldsSchema(config = {}) {
|
|
|
371
371
|
}),
|
|
372
372
|
defineField({
|
|
373
373
|
name: "metaAttributes",
|
|
374
|
-
title:
|
|
374
|
+
// title: 'Additional Meta Attributes',
|
|
375
|
+
...getFieldInfo("metaAttributes", config.fieldOverrides),
|
|
375
376
|
type: "array",
|
|
376
|
-
of: [{ type: "metaAttribute" }]
|
|
377
|
-
description:
|
|
377
|
+
of: [{ type: "metaAttribute" }]
|
|
378
|
+
// description:
|
|
379
|
+
// 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
|
|
378
380
|
}),
|
|
379
381
|
defineField({
|
|
380
382
|
name: "keywords",
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/utils/seoUtils.ts","../src/components/meta/MetaTitle.tsx","../src/components/meta/MetaDescription.tsx","../src/components/SeoPreview.tsx","../src/utils/fieldsUtils.ts","../src/schemas/index.ts","../src/schemas/types/metaAttribute/index.ts","../src/components/openGraph/OgTitle.tsx","../src/components/openGraph/OgDescription.tsx","../src/schemas/types/openGraph/index.ts","../src/components/twitter/twitterTitle.tsx","../src/components/twitter/twitterDescription.tsx","../src/schemas/types/twitter/index.ts","../src/schemas/types/robots/index.ts","../src/schemas/types/metaTag/index.ts","../src/schemas/types/index.ts","../src/plugin.ts","../src/types.ts"],"sourcesContent":["import {PathSegment, useFormValue} from 'sanity'\n\nexport const stopWords = ['the', 'a', 'an', 'and', 'or', 'but']\n\nexport const hasMatchingKeyword = (title: string, keywordList: string[]): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()))\n}\n\nexport const hasKeywordOveruse = (\n title: string,\n keywordList: string[],\n maxOccurrences = 3,\n): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => {\n if (!keyword) return false\n const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), 'g'))\n return matches ? matches.length > maxOccurrences : false\n })\n}\n\nexport const startsWithStopWord = (title: string): boolean => {\n if (!title) return false\n const firstWord = title.trim().split(' ')[0].toLowerCase()\n return stopWords.includes(firstWord)\n}\n\nexport const primaryKeywordAtStart = (title: string, keywords: string[]): boolean => {\n if (!title || keywords.length === 0) return true\n return title.toLowerCase().startsWith(keywords[0].toLowerCase())\n}\n\nexport const truncate = (text: string, maxLength: number) =>\n text.length > maxLength ? text.slice(0, maxLength) + '…' : text\n\nexport const hasExcessivePunctuation = (title: string): boolean => /[!@#$%^&*]{2,}/.test(title)\n\nexport const getMetaTitleValidationMessages = (\n title: string,\n keywords: string[],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n\n const minChar = 50\n const maxChar = 60\n const charCount = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'Meta Title is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Title is ${charCount} characters — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Title is ${charCount} characters — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else feedback.push({text: `Title length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in title — good job!'\n : 'Keywords defined but missing in title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word check\n if (startsWithStopWord(title))\n feedback.push({text: 'Title starts with a stop word — consider rephrasing.', color: 'orange'})\n\n // Punctuation check\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getMetaDescriptionValidationMessages = (\n description: string,\n keywords: string[],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n\n const minChar = 150\n const maxChar = 160\n const charCount = description?.length || 0\n\n if (!description?.trim()) {\n feedback.push({text: 'Meta description is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Description is ${charCount} chars — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Description is ${charCount} chars — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else\n feedback.push({text: `Description length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(description, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in description — good job!'\n : 'Keywords defined but missing in description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(description, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word / filler check\n if (startsWithStopWord(description))\n feedback.push({\n text: 'Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n // Punctuation / special characters\n if (hasExcessivePunctuation(description))\n feedback.push({\n text: 'Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getOgTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 40\n const max = 60\n const count = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'OG Title is empty. Add content for better social preview.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({text: `OG Title is ${count} chars — exceeds recommended ${max}.`, color: 'red'})\n else feedback.push({text: `OG Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG title — good job!'\n : 'Keywords defined but missing in OG title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG title — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(title))\n feedback.push({\n text: 'OG Title starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'OG Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getOgDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 90\n const max = 120\n const count = desc?.length || 0\n\n // Empty check\n if (!desc?.trim()) {\n feedback.push({\n text: 'OG Description is empty. Add content for better social preview.',\n color: 'red',\n })\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `OG Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `OG Description length (${count}) looks good.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG description — good job!'\n : 'Keywords defined but missing in OG description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(desc, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG description — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(desc))\n feedback.push({\n text: 'OG Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(desc))\n feedback.push({\n text: 'OG Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getTwitterTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 30\n const max = 70\n const count = title?.length || 0\n\n if (!title?.trim()) {\n feedback.push({text: 'Twitter Title is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `Twitter Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `Twitter Title is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `Twitter Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in Twitter title — good job!'\n : 'Keywords defined but missing in Twitter title.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(title))\n feedback.push({text: 'Twitter Title has excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getTwitterDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 50\n const max = 200\n const count = desc?.length || 0\n\n if (!desc?.trim()) {\n feedback.push({text: 'Twitter Description is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `Twitter Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `Twitter Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `Twitter Description length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in Twitter description — good job!'\n : 'Keywords defined but missing in Twitter description.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(desc))\n feedback.push({\n text: 'Twitter Description has excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n","import {StringInputProps, useFormValue, useClient, set} from 'sanity'\nimport React, {useEffect, useMemo} from 'react'\nimport {Stack, Text} from '@sanity/ui'\nimport {getMetaTitleValidationMessages} from '../../utils/seoUtils'\n\nconst MetaTitle = (props: StringInputProps) => {\n const client = useClient({apiVersion: '2024-05-05'})\n const {value, onChange, renderDefault, path} = props\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n // Fetch home page title if empty\n // useEffect(() => {\n // if (value) return\n // const fetchData = async () => {\n // const data = await client.fetch(\"*[_type=='homePage'][0]{'title':seo.title}\")\n // if (data?.title && !value) onChange(set(data.title))\n // }\n // fetchData()\n // }, [client, onChange, value])\n\n const feedbackItems = useMemo(\n () => getMetaTitleValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaTitle\n","import {StringInputProps, useFormValue, useClient, set} from 'sanity'\nimport React, {useEffect, useMemo} from 'react'\nimport {Stack, Text} from '@sanity/ui'\nimport {getMetaDescriptionValidationMessages} from '../../utils/seoUtils'\n\nconst MetaDescription = (props: StringInputProps) => {\n const client = useClient({apiVersion: '2024-05-05'})\n const {value, onChange, renderDefault, path} = props\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n // Fetch default meta description from home page if empty\n // useEffect(() => {\n // if (value) return\n // const fetchData = async () => {\n // const data = await client.fetch(\"*[_type=='homePage'][0]{'description':seo.description}\")\n // if (data?.description && !value) onChange(set(data.description))\n // }\n // fetchData()\n // }, [client, onChange, value])\n\n const feedbackItems = useMemo(\n () => getMetaDescriptionValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{width: 10, height: 10, borderRadius: '50%', backgroundColor: item.color}}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaDescription\n","import React from 'react'\nimport {Stack, Text, Box} from '@sanity/ui'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {truncate} from '../utils/seoUtils'\n\ninterface SeoPreviewProps {\n title?: string\n description?: string\n url?: string\n}\n\nconst SeoPreview = (props: StringInputProps) => {\n const {path} = props\n const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n console.log('SEO Preview Parent:', parent)\n\n const {\n title: title,\n description: description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n return (\n <Box\n padding={3}\n style={{\n maxWidth: 600,\n fontFamily: 'Arial, sans-serif',\n }}\n >\n <Stack space={3}>\n {/* URL */}\n <Text size={1} style={{color: '#006621', fontSize: 12, lineHeight: 1.3, marginBottom: 3}}>\n {url || 'https://www.example.com/page-url'}\n </Text>\n\n {/* Title */}\n <Text\n size={3}\n weight=\"bold\"\n style={{\n color: '#1a0dab',\n fontSize: 18,\n lineHeight: 1.4,\n marginBottom: 3,\n }}\n >\n {title ? truncate(title, 60) : 'Meta Title Preview'}\n </Text>\n\n {/* Description */}\n <Text\n size={2}\n style={{\n color: '#545454',\n fontSize: 14,\n lineHeight: 1.6,\n }}\n >\n {description ? truncate(description, 160) : 'Meta description will appear here…'}\n </Text>\n </Stack>\n </Box>\n )\n}\n\nexport default SeoPreview\n","// import {defineField, defineType} from 'sanity'\n// import OgTitle from '../../../components/openGraph/OgTitle'\n// import OgDescription from '../../../components/openGraph/OgDescription'\n\nimport {SeoField, SeoFieldsPluginConfig} from '../plugin'\n\n// export default defineType({\n// name: 'openGraph',\n// title: 'Open Graph Settings',\n// type: 'object',\n// fields: [\n// defineField({\n// name: 'title',\n// title: 'Open Graph Title',\n// type: 'string',\n// components: {\n// input: OgTitle, // Can also wrap with a string input + preview\n// },\n// }),\n// defineField({\n// name: 'description',\n// title: 'Open Graph Description',\n// type: 'text',\n// rows: 3,\n// components: {\n// input: OgDescription, // Can also wrap with a text area + preview\n// },\n// }),\n// defineField({\n// name: 'siteName',\n// title: 'Open Graph Site Name',\n// type: 'string',\n// description: 'The name of your website. This is often the site title.',\n// }),\n// defineField({\n// name: 'type',\n// title: 'Open Graph Type',\n// type: 'string',\n// options: {\n// list: [\n// {title: 'Website', value: 'website'},\n// {title: 'Article', value: 'article'},\n// {title: 'Profile', value: 'profile'},\n// {title: 'Book', value: 'book'},\n// {title: 'Music', value: 'music'},\n// {title: 'Video', value: 'video'},\n// {title: 'Product', value: 'product'},\n// ],\n// // layout: 'radio', // Display as radio buttons\n// },\n// initialValue: 'website',\n// description: 'Select the type of content for Open Graph.',\n// }),\n\n// // upload image or ask for url\n// defineField({\n// name: 'imageType',\n// title: 'Image Type',\n// type: 'string',\n// options: {\n// list: [\n// {title: 'Upload Image', value: 'upload'},\n// {title: 'Image URL', value: 'url'},\n// ],\n// },\n// initialValue: 'upload',\n// }),\n// defineField({\n// name: 'image',\n// title: 'Open Graph Image',\n// type: 'image',\n// options: {\n// hotspot: true,\n// },\n// hidden: ({parent}) => parent?.imageType !== 'upload',\n// description:\n// 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n// }),\n// defineField({\n// name: 'imageUrl',\n// title: 'Open Graph Image URL',\n// type: 'url',\n// hidden: ({parent}) => parent?.imageType !== 'url',\n// description:\n// 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',\n// }),\n// ],\n// })\n\nconst DEFAULT_FIELD_INFO = {\n title: {\n title: 'Meta Title',\n description:\n 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',\n },\n description: {\n title: 'Meta Description',\n description:\n 'Provide a concise summary of the page content. This description may be used by search engines in search results.',\n },\n metaImage: {\n title: 'Meta Image',\n description:\n 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',\n },\n keywords: {\n title: 'Keywords',\n description:\n 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',\n },\n canonicalUrl: {\n title: 'Canonical URL',\n description:\n 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',\n },\n robots: {\n title: 'Robots Settings',\n description: 'Configure how search engine crawlers should index and follow links on this page.',\n },\n metaAttributes: {\n title: 'Additional Meta Attributes',\n description:\n 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\n },\n}\n\nexport const getFieldInfo = (\n fieldName: string,\n fieldOverrides: SeoFieldsPluginConfig['fieldOverrides'],\n) => {\n const fieldInfo =\n (fieldOverrides && fieldOverrides[fieldName as keyof typeof fieldOverrides]) ||\n DEFAULT_FIELD_INFO[fieldName as keyof typeof DEFAULT_FIELD_INFO]\n return fieldInfo\n ? {title: fieldInfo.title, description: fieldInfo.description}\n : {title: '', description: ''}\n}\n","import {defineField, defineType} from 'sanity'\nimport MetaTitle from '../components/meta/MetaTitle'\nimport MetaDescription from '../components/meta/MetaDescription'\nimport SeoPreview from '../components/SeoPreview'\nimport {SeoFieldsPluginConfig} from '../plugin'\nimport {getFieldInfo} from '../utils/fieldsUtils'\n\nexport default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}) {\n return defineType({\n name: 'seoFields',\n title: 'SEO Fields',\n type: 'object',\n fields: [\n defineField({\n name: 'robots',\n title: 'Robots Settings',\n type: 'robots', // Use the separate robots type here\n }),\n // 👇 conditionally spread preview field\n ...(config.seoPreview\n ? []\n : [\n defineField({\n name: 'preview',\n title: 'SEO Preview',\n type: 'string',\n components: {input: SeoPreview},\n readOnly: true,\n }),\n ]),\n\n defineField({\n name: 'title',\n ...getFieldInfo('title', config.fieldOverrides),\n // title: 'Meta Title',\n type: 'string',\n // description:\n // 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',\n components: {\n input: MetaTitle,\n },\n // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),\n }),\n defineField({\n name: 'description',\n ...getFieldInfo('description', config.fieldOverrides),\n // title: 'Meta Description',\n // description:\n // 'Provide a concise summary of the page content. This description may be used by search engines in search results.',\n type: 'text',\n rows: 3,\n components: {\n input: MetaDescription,\n },\n // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),\n }),\n defineField({\n name: 'metaImage',\n ...getFieldInfo('metaImage', config.fieldOverrides),\n // title: 'Meta Image',\n // description:\n // 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',\n type: 'image',\n options: {\n hotspot: true,\n },\n }),\n defineField({\n name: 'metaAttributes',\n title: 'Additional Meta Attributes',\n type: 'array',\n of: [{type: 'metaAttribute'}],\n description:\n 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\n }),\n defineField({\n name: 'keywords',\n ...getFieldInfo('keywords', config.fieldOverrides),\n title: 'Keywords',\n type: 'array',\n of: [{type: 'string'}],\n description:\n 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',\n }),\n defineField({\n name: 'canonicalUrl',\n ...getFieldInfo('canonicalUrl', config.fieldOverrides),\n title: 'Canonical URL',\n type: 'url',\n description:\n 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',\n }),\n defineField({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'openGraph',\n }),\n defineField({\n name: 'twitter',\n title: 'Twitter Card Settings',\n type: 'twitter',\n }),\n ],\n })\n}\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'metaAttribute',\n title: 'Meta Attribute',\n type: 'object',\n fields: [\n defineField({\n name: 'key',\n title: 'Attribute Name',\n type: 'string',\n }),\n defineField({\n name: 'type',\n title: 'Attribute Value Type',\n type: 'string',\n options: {\n list: [\n {title: 'String', value: 'string'},\n {title: 'Image', value: 'image'},\n ],\n },\n initialValue: 'string',\n }),\n defineField({\n name: 'value',\n title: 'Attribute Value',\n type: 'string',\n hidden: ({parent}) => parent?.type === 'image',\n }),\n defineField({\n name: 'image',\n title: 'Attribute Image Value',\n type: 'image',\n hidden: ({parent}) => parent?.type === 'string',\n }),\n ],\n preview: {\n select: {\n attributeName: 'key',\n attributeValueString: 'value',\n attributeValueImage: 'image',\n },\n prepare({\n attributeName,\n attributeValueString,\n attributeValueImage,\n }: {\n attributeName: string\n attributeValueString: string\n attributeValueImage: any\n }) {\n return {\n title: attributeName || 'No Attribute Name',\n subtitle: attributeValueString\n ? `Value: ${attributeValueString}`\n : attributeValueImage\n ? 'Value: [Image]'\n : 'No Attribute Value',\n media: attributeValueImage,\n }\n },\n },\n})\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text, Box} from '@sanity/ui'\nimport { getOgTitleValidation } from '../../utils/seoUtils'\n\nconst OgTitle: React.FC<StringInputProps> = (props) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getOgTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default OgTitle\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getOgDescriptionValidation} from '../../utils/seoUtils'\n\nconst OgDescription = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getOgDescriptionValidation(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n {/* Validation */}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default OgDescription\n","import {defineField, defineType} from 'sanity'\nimport OgTitle from '../../../components/openGraph/OgTitle'\nimport OgDescription from '../../../components/openGraph/OgDescription'\n\nexport default defineType({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'title',\n title: 'Open Graph Title',\n type: 'string',\n components: {\n input: OgTitle, // Can also wrap with a string input + preview\n },\n }),\n defineField({\n name: 'description',\n title: 'Open Graph Description',\n type: 'text',\n rows: 3,\n components: {\n input: OgDescription, // Can also wrap with a text area + preview\n },\n }),\n defineField({\n name: 'siteName',\n title: 'Open Graph Site Name',\n type: 'string',\n description: 'The name of your website. This is often the site title.',\n }),\n defineField({\n name: 'type',\n title: 'Open Graph Type',\n type: 'string',\n options: {\n list: [\n {title: 'Website', value: 'website'},\n {title: 'Article', value: 'article'},\n {title: 'Profile', value: 'profile'},\n {title: 'Book', value: 'book'},\n {title: 'Music', value: 'music'},\n {title: 'Video', value: 'video'},\n {title: 'Product', value: 'product'},\n ],\n // layout: 'radio', // Display as radio buttons\n },\n initialValue: 'website',\n description: 'Select the type of content for Open Graph.',\n }),\n\n // upload image or ask for url\n defineField({\n name: 'imageType',\n title: 'Image Type',\n type: 'string',\n options: {\n list: [\n {title: 'Upload Image', value: 'upload'},\n {title: 'Image URL', value: 'url'},\n ],\n },\n initialValue: 'upload',\n }),\n defineField({\n name: 'image',\n title: 'Open Graph Image',\n type: 'image',\n options: {\n hotspot: true,\n },\n hidden: ({parent}) => parent?.imageType !== 'upload',\n description:\n 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n }),\n defineField({\n name: 'imageUrl',\n title: 'Open Graph Image URL',\n type: 'url',\n hidden: ({parent}) => parent?.imageType !== 'url',\n description:\n 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',\n }),\n ],\n})\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getTwitterTitleValidation} from '../../utils/seoUtils'\n\nconst TwitterTitle = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getTwitterTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default TwitterTitle\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getTwitterDescriptionValidation} from '../../utils/seoUtils'\n\nconst TwitterDescription = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getTwitterDescriptionValidation(value || '', keywords,isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default TwitterDescription\n","import {defineField, defineType} from 'sanity'\nimport TwitterTitle from '../../../components/twitter/twitterTitle'\nimport TwitterDescription from '../../../components/twitter/twitterDescription'\n\nexport default defineType({\n name: 'twitter',\n title: 'Twitter',\n type: 'object',\n fields: [\n defineField({\n name: 'card',\n title: 'Card Type',\n type: 'string',\n options: {\n list: [\n {title: 'Summary', value: 'summary'},\n {title: 'Summary with Large Image', value: 'summary_large_image'},\n {title: 'App', value: 'app'},\n {title: 'Player', value: 'player'},\n ],\n },\n initialValue: 'summary_large_image', // good default\n }),\n defineField({\n name: 'site',\n title: 'Site Twitter Handle',\n type: 'string',\n description: 'The Twitter handle of the website (e.g., @example)',\n }),\n defineField({\n name: 'title',\n title: 'Twitter Title',\n type: 'string',\n description: 'The title of the content as it should appear on Twitter.',\n components: {\n input: TwitterTitle,\n },\n }),\n defineField({\n name: 'description',\n title: 'Twitter Description',\n type: 'text',\n rows: 3,\n description: 'A brief description of the content for Twitter.',\n components: {\n input: TwitterDescription,\n },\n }),\n defineField({\n name: 'image',\n title: 'Twitter Image',\n type: 'image',\n description:\n 'An image URL which should be at least 120x120px for \"summary\" card and 280x150px for \"summary_large_image\" card.',\n options: {\n hotspot: true,\n },\n fields: [\n defineField({\n name: 'alt',\n title: 'Image Alt Text',\n type: 'string',\n description: 'Short alt text describing the image',\n }),\n ],\n }),\n ],\n})\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'robots',\n title: 'Robots Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'noIndex',\n title: 'No Index',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from indexing this page. The page will not appear in search results.',\n }),\n defineField({\n name: 'noFollow',\n title: 'No Follow',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from following links on this page. Links will not pass SEO value.',\n }),\n ],\n description: 'Select how search engines should index and follow links on this page.',\n})\n","import { defineType } from \"sanity\";\n\nexport default defineType({\n name: \"metaTag\",\n title: \"Meta Tag\",\n type: \"object\",\n fields: [\n {\n name: \"metaAttributes\",\n title: \"Meta Attributes\",\n type: \"array\",\n of: [{ type: \"metaAttribute\" }],\n description: \"Add custom meta attributes to the head of the document for additional SEO and social media integration.\",\n },\n ],\n})","import seoFields from '..'\nimport metaAttribute from './metaAttribute'\nimport openGraph from './openGraph'\nimport twitter from './twitter'\nimport robots from './robots'\nimport metaTag from './metaTag'\nimport {SeoFieldsPluginConfig} from '../../plugin'\n\nexport default function types(config: SeoFieldsPluginConfig = {}) {\n return [\n seoFields(config), // pass config here\n openGraph,\n twitter,\n metaAttribute,\n metaTag,\n robots,\n ]\n}\n","// plugin.ts\nimport {definePlugin} from 'sanity'\nimport types from './schemas/types'\n\nexport interface SeoField {\n title?: string\n description?: string\n}\n\nexport type SeoFieldKeys = 'title' | 'description' | 'canonicalUrl' | 'metaImage' | 'keywords'\n\nexport interface SeoFieldsPluginConfig {\n seoPreview?: boolean\n fieldOverrides?: {\n [key in SeoFieldKeys]?: SeoField\n }\n}\n\nconst seofields = definePlugin<SeoFieldsPluginConfig | void>((config = {}) => {\n return {\n name: 'sanity-plugin-seofields',\n schema: {\n types: types(config as SeoFieldsPluginConfig), // pass config down to schemas\n },\n }\n})\n\nexport default seofields\n","// TypeScript interfaces for SEO Fields Plugin\n\n// Base Sanity types\nexport interface SanityImage {\n _type: 'image'\n asset: {\n _ref: string\n _type: 'reference'\n }\n hotspot?: {\n x: number\n y: number\n height: number\n width: number\n }\n crop?: {\n top: number\n bottom: number\n left: number\n right: number\n }\n alt?: string\n}\n\nexport interface SanityImageWithAlt extends SanityImage {\n alt: string\n}\n\n// Robots settings\nexport interface RobotsSettings {\n noIndex?: boolean\n noFollow?: boolean\n}\n\n// Meta Attribute\nexport interface MetaAttribute {\n _type: 'metaAttribute'\n key: string\n type: 'string' | 'image'\n value?: string\n image?: SanityImage\n}\n\n// Open Graph settings\nexport interface OpenGraphSettings {\n _type: 'openGraph'\n title?: string\n description?: string\n siteName?: string\n type?: 'website' | 'article' | 'profile' | 'book' | 'music' | 'video' | 'product'\n imageType?: 'upload' | 'url'\n image?: SanityImage\n imageUrl?: string\n}\n\n// Twitter Card settings\nexport interface TwitterCardSettings {\n _type: 'twitter'\n card?: 'summary' | 'summary_large_image' | 'app' | 'player'\n site?: string\n title?: string\n description?: string\n image?: SanityImageWithAlt\n}\n\n// Main SEO Fields interface\nexport interface SeoFields {\n _type: 'seoFields'\n robots?: RobotsSettings\n preview?: string\n title?: string\n description?: string\n metaImage?: SanityImage\n keywords?: string[]\n canonicalUrl?: string\n openGraph?: OpenGraphSettings\n twitter?: TwitterCardSettings\n}\n\n// Type guards\nexport const isSeoFields = (obj: any): obj is SeoFields => {\n return obj && obj._type === 'seoFields'\n}\n\nexport const isOpenGraphSettings = (obj: any): obj is OpenGraphSettings => {\n return obj && obj._type === 'openGraph'\n}\n\nexport const isTwitterCardSettings = (obj: any): obj is TwitterCardSettings => {\n return obj && obj._type === 'twitter'\n}\n\nexport const isMetaAttribute = (obj: any): obj is MetaAttribute => {\n return obj && obj._type === 'metaAttribute'\n}\n\n// Utility types for form validation\nexport interface SeoValidationRules {\n title: {\n maxLength: number\n warningLength: number\n }\n description: {\n maxLength: number\n warningLength: number\n }\n openGraphTitle: {\n maxLength: number\n }\n openGraphDescription: {\n maxLength: number\n }\n twitterTitle: {\n maxLength: number\n }\n twitterDescription: {\n maxLength: number\n }\n}\n\nexport const defaultSeoValidationRules: SeoValidationRules = {\n title: {\n maxLength: 70,\n warningLength: 60,\n },\n description: {\n maxLength: 160,\n warningLength: 150,\n },\n openGraphTitle: {\n maxLength: 95,\n },\n openGraphDescription: {\n maxLength: 200,\n },\n twitterTitle: {\n maxLength: 70,\n },\n twitterDescription: {\n maxLength: 200,\n },\n}\n\n// All types are already exported above with individual export statements\n"],"names":["seoFields"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/utils/seoUtils.ts","../src/components/meta/MetaTitle.tsx","../src/components/meta/MetaDescription.tsx","../src/components/SeoPreview.tsx","../src/utils/fieldsUtils.ts","../src/schemas/index.ts","../src/schemas/types/metaAttribute/index.ts","../src/components/openGraph/OgTitle.tsx","../src/components/openGraph/OgDescription.tsx","../src/schemas/types/openGraph/index.ts","../src/components/twitter/twitterTitle.tsx","../src/components/twitter/twitterDescription.tsx","../src/schemas/types/twitter/index.ts","../src/schemas/types/robots/index.ts","../src/schemas/types/metaTag/index.ts","../src/schemas/types/index.ts","../src/plugin.ts","../src/types.ts"],"sourcesContent":["import {PathSegment, useFormValue} from 'sanity'\n\nexport const stopWords = ['the', 'a', 'an', 'and', 'or', 'but']\n\nexport const hasMatchingKeyword = (title: string, keywordList: string[]): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()))\n}\n\nexport const hasKeywordOveruse = (\n title: string,\n keywordList: string[],\n maxOccurrences = 3,\n): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => {\n if (!keyword) return false\n const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), 'g'))\n return matches ? matches.length > maxOccurrences : false\n })\n}\n\nexport const startsWithStopWord = (title: string): boolean => {\n if (!title) return false\n const firstWord = title.trim().split(' ')[0].toLowerCase()\n return stopWords.includes(firstWord)\n}\n\nexport const primaryKeywordAtStart = (title: string, keywords: string[]): boolean => {\n if (!title || keywords.length === 0) return true\n return title.toLowerCase().startsWith(keywords[0].toLowerCase())\n}\n\nexport const truncate = (text: string, maxLength: number) =>\n text.length > maxLength ? text.slice(0, maxLength) + '…' : text\n\nexport const hasExcessivePunctuation = (title: string): boolean => /[!@#$%^&*]{2,}/.test(title)\n\nexport const getMetaTitleValidationMessages = (\n title: string,\n keywords: string[],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n\n const minChar = 50\n const maxChar = 60\n const charCount = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'Meta Title is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Title is ${charCount} characters — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Title is ${charCount} characters — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else feedback.push({text: `Title length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in title — good job!'\n : 'Keywords defined but missing in title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word check\n if (startsWithStopWord(title))\n feedback.push({text: 'Title starts with a stop word — consider rephrasing.', color: 'orange'})\n\n // Punctuation check\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getMetaDescriptionValidationMessages = (\n description: string,\n keywords: string[],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n\n const minChar = 150\n const maxChar = 160\n const charCount = description?.length || 0\n\n if (!description?.trim()) {\n feedback.push({text: 'Meta description is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Description is ${charCount} chars — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Description is ${charCount} chars — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else\n feedback.push({text: `Description length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(description, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in description — good job!'\n : 'Keywords defined but missing in description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(description, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word / filler check\n if (startsWithStopWord(description))\n feedback.push({\n text: 'Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n // Punctuation / special characters\n if (hasExcessivePunctuation(description))\n feedback.push({\n text: 'Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getOgTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 40\n const max = 60\n const count = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'OG Title is empty. Add content for better social preview.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({text: `OG Title is ${count} chars — exceeds recommended ${max}.`, color: 'red'})\n else feedback.push({text: `OG Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG title — good job!'\n : 'Keywords defined but missing in OG title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG title — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(title))\n feedback.push({\n text: 'OG Title starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'OG Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getOgDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 90\n const max = 120\n const count = desc?.length || 0\n\n // Empty check\n if (!desc?.trim()) {\n feedback.push({\n text: 'OG Description is empty. Add content for better social preview.',\n color: 'red',\n })\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `OG Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `OG Description length (${count}) looks good.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG description — good job!'\n : 'Keywords defined but missing in OG description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(desc, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG description — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(desc))\n feedback.push({\n text: 'OG Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(desc))\n feedback.push({\n text: 'OG Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getTwitterTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 30\n const max = 70\n const count = title?.length || 0\n\n if (!title?.trim()) {\n feedback.push({text: 'Twitter Title is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `Twitter Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `Twitter Title is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `Twitter Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in Twitter title — good job!'\n : 'Keywords defined but missing in Twitter title.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(title))\n feedback.push({text: 'Twitter Title has excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getTwitterDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n) => {\n const feedback: {text: string; color: 'green' | 'orange' | 'red'}[] = []\n const min = 50\n const max = 200\n const count = desc?.length || 0\n\n if (!desc?.trim()) {\n feedback.push({text: 'Twitter Description is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `Twitter Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `Twitter Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `Twitter Description length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in Twitter description — good job!'\n : 'Keywords defined but missing in Twitter description.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(desc))\n feedback.push({\n text: 'Twitter Description has excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n","import {StringInputProps, useFormValue, useClient, set} from 'sanity'\nimport React, {useEffect, useMemo} from 'react'\nimport {Stack, Text} from '@sanity/ui'\nimport {getMetaTitleValidationMessages} from '../../utils/seoUtils'\n\nconst MetaTitle = (props: StringInputProps) => {\n const client = useClient({apiVersion: '2024-05-05'})\n const {value, onChange, renderDefault, path} = props\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n // Fetch home page title if empty\n // useEffect(() => {\n // if (value) return\n // const fetchData = async () => {\n // const data = await client.fetch(\"*[_type=='homePage'][0]{'title':seo.title}\")\n // if (data?.title && !value) onChange(set(data.title))\n // }\n // fetchData()\n // }, [client, onChange, value])\n\n const feedbackItems = useMemo(\n () => getMetaTitleValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaTitle\n","import {StringInputProps, useFormValue, useClient, set} from 'sanity'\nimport React, {useEffect, useMemo} from 'react'\nimport {Stack, Text} from '@sanity/ui'\nimport {getMetaDescriptionValidationMessages} from '../../utils/seoUtils'\n\nconst MetaDescription = (props: StringInputProps) => {\n const client = useClient({apiVersion: '2024-05-05'})\n const {value, onChange, renderDefault, path} = props\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n // Fetch default meta description from home page if empty\n // useEffect(() => {\n // if (value) return\n // const fetchData = async () => {\n // const data = await client.fetch(\"*[_type=='homePage'][0]{'description':seo.description}\")\n // if (data?.description && !value) onChange(set(data.description))\n // }\n // fetchData()\n // }, [client, onChange, value])\n\n const feedbackItems = useMemo(\n () => getMetaDescriptionValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{width: 10, height: 10, borderRadius: '50%', backgroundColor: item.color}}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaDescription\n","import React from 'react'\nimport {Stack, Text, Box} from '@sanity/ui'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {truncate} from '../utils/seoUtils'\n\ninterface SeoPreviewProps {\n title?: string\n description?: string\n url?: string\n}\n\nconst SeoPreview = (props: StringInputProps) => {\n const {path} = props\n const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n console.log('SEO Preview Parent:', parent)\n\n const {\n title: title,\n description: description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n return (\n <Box\n padding={3}\n style={{\n maxWidth: 600,\n fontFamily: 'Arial, sans-serif',\n }}\n >\n <Stack space={3}>\n {/* URL */}\n <Text size={1} style={{color: '#006621', fontSize: 12, lineHeight: 1.3, marginBottom: 3}}>\n {url || 'https://www.example.com/page-url'}\n </Text>\n\n {/* Title */}\n <Text\n size={3}\n weight=\"bold\"\n style={{\n color: '#1a0dab',\n fontSize: 18,\n lineHeight: 1.4,\n marginBottom: 3,\n }}\n >\n {title ? truncate(title, 60) : 'Meta Title Preview'}\n </Text>\n\n {/* Description */}\n <Text\n size={2}\n style={{\n color: '#545454',\n fontSize: 14,\n lineHeight: 1.6,\n }}\n >\n {description ? truncate(description, 160) : 'Meta description will appear here…'}\n </Text>\n </Stack>\n </Box>\n )\n}\n\nexport default SeoPreview\n","// import {defineField, defineType} from 'sanity'\n// import OgTitle from '../../../components/openGraph/OgTitle'\n// import OgDescription from '../../../components/openGraph/OgDescription'\n\nimport {SeoFieldsPluginConfig} from '../plugin'\n\n// export default defineType({\n// name: 'openGraph',\n// title: 'Open Graph Settings',\n// type: 'object',\n// fields: [\n// defineField({\n// name: 'title',\n// title: 'Open Graph Title',\n// type: 'string',\n// components: {\n// input: OgTitle, // Can also wrap with a string input + preview\n// },\n// }),\n// defineField({\n// name: 'description',\n// title: 'Open Graph Description',\n// type: 'text',\n// rows: 3,\n// components: {\n// input: OgDescription, // Can also wrap with a text area + preview\n// },\n// }),\n// defineField({\n// name: 'siteName',\n// title: 'Open Graph Site Name',\n// type: 'string',\n// description: 'The name of your website. This is often the site title.',\n// }),\n// defineField({\n// name: 'type',\n// title: 'Open Graph Type',\n// type: 'string',\n// options: {\n// list: [\n// {title: 'Website', value: 'website'},\n// {title: 'Article', value: 'article'},\n// {title: 'Profile', value: 'profile'},\n// {title: 'Book', value: 'book'},\n// {title: 'Music', value: 'music'},\n// {title: 'Video', value: 'video'},\n// {title: 'Product', value: 'product'},\n// ],\n// // layout: 'radio', // Display as radio buttons\n// },\n// initialValue: 'website',\n// description: 'Select the type of content for Open Graph.',\n// }),\n\n// // upload image or ask for url\n// defineField({\n// name: 'imageType',\n// title: 'Image Type',\n// type: 'string',\n// options: {\n// list: [\n// {title: 'Upload Image', value: 'upload'},\n// {title: 'Image URL', value: 'url'},\n// ],\n// },\n// initialValue: 'upload',\n// }),\n// defineField({\n// name: 'image',\n// title: 'Open Graph Image',\n// type: 'image',\n// options: {\n// hotspot: true,\n// },\n// hidden: ({parent}) => parent?.imageType !== 'upload',\n// description:\n// 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n// }),\n// defineField({\n// name: 'imageUrl',\n// title: 'Open Graph Image URL',\n// type: 'url',\n// hidden: ({parent}) => parent?.imageType !== 'url',\n// description:\n// 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',\n// }),\n// ],\n// })\n\nconst DEFAULT_FIELD_INFO = {\n title: {\n title: 'Meta Title',\n description:\n 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',\n },\n description: {\n title: 'Meta Description',\n description:\n 'Provide a concise summary of the page content. This description may be used by search engines in search results.',\n },\n metaImage: {\n title: 'Meta Image',\n description:\n 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',\n },\n keywords: {\n title: 'Keywords',\n description:\n 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',\n },\n canonicalUrl: {\n title: 'Canonical URL',\n description:\n 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',\n },\n robots: {\n title: 'Robots Settings',\n description: 'Configure how search engine crawlers should index and follow links on this page.',\n },\n metaAttributes: {\n title: 'Additional Meta Attributes',\n description:\n 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\n },\n}\n\nexport const getFieldInfo = (\n fieldName: string,\n fieldOverrides: SeoFieldsPluginConfig['fieldOverrides'],\n) => {\n const fieldInfo =\n (fieldOverrides && fieldOverrides[fieldName as keyof typeof fieldOverrides]) ||\n DEFAULT_FIELD_INFO[fieldName as keyof typeof DEFAULT_FIELD_INFO]\n return fieldInfo\n ? {title: fieldInfo.title, description: fieldInfo.description}\n : {title: '', description: ''}\n}\n","import {defineField, defineType} from 'sanity'\nimport MetaTitle from '../components/meta/MetaTitle'\nimport MetaDescription from '../components/meta/MetaDescription'\nimport SeoPreview from '../components/SeoPreview'\nimport {SeoFieldsPluginConfig} from '../plugin'\nimport {getFieldInfo} from '../utils/fieldsUtils'\n\nexport default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}) {\n return defineType({\n name: 'seoFields',\n title: 'SEO Fields',\n type: 'object',\n fields: [\n defineField({\n name: 'robots',\n title: 'Robots Settings',\n type: 'robots', // Use the separate robots type here\n }),\n // 👇 conditionally spread preview field\n ...(config.seoPreview\n ? []\n : [\n defineField({\n name: 'preview',\n title: 'SEO Preview',\n type: 'string',\n components: {input: SeoPreview},\n readOnly: true,\n }),\n ]),\n\n defineField({\n name: 'title',\n ...getFieldInfo('title', config.fieldOverrides),\n // title: 'Meta Title',\n type: 'string',\n // description:\n // 'The meta title is displayed in search engine results as the clickable headline for a given result. It should be concise and accurately reflect the content of the page.',\n components: {\n input: MetaTitle,\n },\n // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),\n }),\n defineField({\n name: 'description',\n ...getFieldInfo('description', config.fieldOverrides),\n // title: 'Meta Description',\n // description:\n // 'Provide a concise summary of the page content. This description may be used by search engines in search results.',\n type: 'text',\n rows: 3,\n components: {\n input: MetaDescription,\n },\n // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),\n }),\n defineField({\n name: 'metaImage',\n ...getFieldInfo('metaImage', config.fieldOverrides),\n // title: 'Meta Image',\n // description:\n // 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',\n type: 'image',\n options: {\n hotspot: true,\n },\n }),\n defineField({\n name: 'metaAttributes',\n // title: 'Additional Meta Attributes',\n ...getFieldInfo('metaAttributes', config.fieldOverrides),\n type: 'array',\n of: [{type: 'metaAttribute'}],\n // description:\n // 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\n }),\n defineField({\n name: 'keywords',\n ...getFieldInfo('keywords', config.fieldOverrides),\n title: 'Keywords',\n type: 'array',\n of: [{type: 'string'}],\n description:\n 'Add relevant keywords for this page. These keywords will be used for SEO purposes.',\n }),\n defineField({\n name: 'canonicalUrl',\n ...getFieldInfo('canonicalUrl', config.fieldOverrides),\n title: 'Canonical URL',\n type: 'url',\n description:\n 'Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.',\n }),\n defineField({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'openGraph',\n }),\n defineField({\n name: 'twitter',\n title: 'Twitter Card Settings',\n type: 'twitter',\n }),\n ],\n })\n}\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'metaAttribute',\n title: 'Meta Attribute',\n type: 'object',\n fields: [\n defineField({\n name: 'key',\n title: 'Attribute Name',\n type: 'string',\n }),\n defineField({\n name: 'type',\n title: 'Attribute Value Type',\n type: 'string',\n options: {\n list: [\n {title: 'String', value: 'string'},\n {title: 'Image', value: 'image'},\n ],\n },\n initialValue: 'string',\n }),\n defineField({\n name: 'value',\n title: 'Attribute Value',\n type: 'string',\n hidden: ({parent}) => parent?.type === 'image',\n }),\n defineField({\n name: 'image',\n title: 'Attribute Image Value',\n type: 'image',\n hidden: ({parent}) => parent?.type === 'string',\n }),\n ],\n preview: {\n select: {\n attributeName: 'key',\n attributeValueString: 'value',\n attributeValueImage: 'image',\n },\n prepare({\n attributeName,\n attributeValueString,\n attributeValueImage,\n }: {\n attributeName: string\n attributeValueString: string\n attributeValueImage: any\n }) {\n return {\n title: attributeName || 'No Attribute Name',\n subtitle: attributeValueString\n ? `Value: ${attributeValueString}`\n : attributeValueImage\n ? 'Value: [Image]'\n : 'No Attribute Value',\n media: attributeValueImage,\n }\n },\n },\n})\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text, Box} from '@sanity/ui'\nimport { getOgTitleValidation } from '../../utils/seoUtils'\n\nconst OgTitle: React.FC<StringInputProps> = (props) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getOgTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default OgTitle\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getOgDescriptionValidation} from '../../utils/seoUtils'\n\nconst OgDescription = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getOgDescriptionValidation(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n {/* Validation */}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default OgDescription\n","import {defineField, defineType} from 'sanity'\nimport OgTitle from '../../../components/openGraph/OgTitle'\nimport OgDescription from '../../../components/openGraph/OgDescription'\n\nexport default defineType({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'title',\n title: 'Open Graph Title',\n type: 'string',\n components: {\n input: OgTitle, // Can also wrap with a string input + preview\n },\n }),\n defineField({\n name: 'description',\n title: 'Open Graph Description',\n type: 'text',\n rows: 3,\n components: {\n input: OgDescription, // Can also wrap with a text area + preview\n },\n }),\n defineField({\n name: 'siteName',\n title: 'Open Graph Site Name',\n type: 'string',\n description: 'The name of your website. This is often the site title.',\n }),\n defineField({\n name: 'type',\n title: 'Open Graph Type',\n type: 'string',\n options: {\n list: [\n {title: 'Website', value: 'website'},\n {title: 'Article', value: 'article'},\n {title: 'Profile', value: 'profile'},\n {title: 'Book', value: 'book'},\n {title: 'Music', value: 'music'},\n {title: 'Video', value: 'video'},\n {title: 'Product', value: 'product'},\n ],\n // layout: 'radio', // Display as radio buttons\n },\n initialValue: 'website',\n description: 'Select the type of content for Open Graph.',\n }),\n\n // upload image or ask for url\n defineField({\n name: 'imageType',\n title: 'Image Type',\n type: 'string',\n options: {\n list: [\n {title: 'Upload Image', value: 'upload'},\n {title: 'Image URL', value: 'url'},\n ],\n },\n initialValue: 'upload',\n }),\n defineField({\n name: 'image',\n title: 'Open Graph Image',\n type: 'image',\n options: {\n hotspot: true,\n },\n hidden: ({parent}) => parent?.imageType !== 'upload',\n description:\n 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n }),\n defineField({\n name: 'imageUrl',\n title: 'Open Graph Image URL',\n type: 'url',\n hidden: ({parent}) => parent?.imageType !== 'url',\n description:\n 'Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px).',\n }),\n ],\n})\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getTwitterTitleValidation} from '../../utils/seoUtils'\n\nconst TwitterTitle = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getTwitterTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default TwitterTitle\n","import React, {useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {Stack, Text} from '@sanity/ui'\nimport {getTwitterDescriptionValidation} from '../../utils/seoUtils'\n\nconst TwitterDescription = (props: StringInputProps) => {\n const {value, renderDefault, path} = props\n\n // Access parent object to get keywords\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = parent?.keywords || []\n\n const feedbackItems = useMemo(\n () => getTwitterDescriptionValidation(value || '', keywords,isParentseoField),\n [value, keywords],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: any) => (\n <div key={item.text} style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: item.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n {item.text}\n </Text>\n </div>\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default TwitterDescription\n","import {defineField, defineType} from 'sanity'\nimport TwitterTitle from '../../../components/twitter/twitterTitle'\nimport TwitterDescription from '../../../components/twitter/twitterDescription'\n\nexport default defineType({\n name: 'twitter',\n title: 'Twitter',\n type: 'object',\n fields: [\n defineField({\n name: 'card',\n title: 'Card Type',\n type: 'string',\n options: {\n list: [\n {title: 'Summary', value: 'summary'},\n {title: 'Summary with Large Image', value: 'summary_large_image'},\n {title: 'App', value: 'app'},\n {title: 'Player', value: 'player'},\n ],\n },\n initialValue: 'summary_large_image', // good default\n }),\n defineField({\n name: 'site',\n title: 'Site Twitter Handle',\n type: 'string',\n description: 'The Twitter handle of the website (e.g., @example)',\n }),\n defineField({\n name: 'title',\n title: 'Twitter Title',\n type: 'string',\n description: 'The title of the content as it should appear on Twitter.',\n components: {\n input: TwitterTitle,\n },\n }),\n defineField({\n name: 'description',\n title: 'Twitter Description',\n type: 'text',\n rows: 3,\n description: 'A brief description of the content for Twitter.',\n components: {\n input: TwitterDescription,\n },\n }),\n defineField({\n name: 'image',\n title: 'Twitter Image',\n type: 'image',\n description:\n 'An image URL which should be at least 120x120px for \"summary\" card and 280x150px for \"summary_large_image\" card.',\n options: {\n hotspot: true,\n },\n fields: [\n defineField({\n name: 'alt',\n title: 'Image Alt Text',\n type: 'string',\n description: 'Short alt text describing the image',\n }),\n ],\n }),\n ],\n})\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'robots',\n title: 'Robots Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'noIndex',\n title: 'No Index',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from indexing this page. The page will not appear in search results.',\n }),\n defineField({\n name: 'noFollow',\n title: 'No Follow',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from following links on this page. Links will not pass SEO value.',\n }),\n ],\n description: 'Select how search engines should index and follow links on this page.',\n})\n","import { defineType } from \"sanity\";\n\nexport default defineType({\n name: \"metaTag\",\n title: \"Meta Tag\",\n type: \"object\",\n fields: [\n {\n name: \"metaAttributes\",\n title: \"Meta Attributes\",\n type: \"array\",\n of: [{ type: \"metaAttribute\" }],\n description: \"Add custom meta attributes to the head of the document for additional SEO and social media integration.\",\n },\n ],\n})","import seoFields from '..'\nimport metaAttribute from './metaAttribute'\nimport openGraph from './openGraph'\nimport twitter from './twitter'\nimport robots from './robots'\nimport metaTag from './metaTag'\nimport {SeoFieldsPluginConfig} from '../../plugin'\n\nexport default function types(config: SeoFieldsPluginConfig = {}) {\n return [\n seoFields(config), // pass config here\n openGraph,\n twitter,\n metaAttribute,\n metaTag,\n robots,\n ]\n}\n","// plugin.ts\nimport {definePlugin} from 'sanity'\nimport types from './schemas/types'\n\nexport interface SeoFieldConfig {\n title?: string\n description?: string\n}\n\nexport type SeoFieldKeys =\n | 'title'\n | 'description'\n | 'canonicalUrl'\n | 'metaImage'\n | 'keywords'\n | 'metaAttributes'\n\nexport interface SeoFieldsPluginConfig {\n seoPreview?: boolean\n fieldOverrides?: {\n [key in SeoFieldKeys]?: SeoFieldConfig\n }\n}\n\nconst seofields = definePlugin<SeoFieldsPluginConfig | void>((config = {}) => {\n return {\n name: 'sanity-plugin-seofields',\n schema: {\n types: types(config as SeoFieldsPluginConfig), // pass config down to schemas\n },\n }\n})\n\nexport default seofields\n","// TypeScript interfaces for SEO Fields Plugin\n\n// Base Sanity types\nexport interface SanityImage {\n _type: 'image'\n asset: {\n _ref: string\n _type: 'reference'\n }\n hotspot?: {\n x: number\n y: number\n height: number\n width: number\n }\n crop?: {\n top: number\n bottom: number\n left: number\n right: number\n }\n alt?: string\n}\n\nexport interface SanityImageWithAlt extends SanityImage {\n alt: string\n}\n\n// Robots settings\nexport interface RobotsSettings {\n noIndex?: boolean\n noFollow?: boolean\n}\n\n// Meta Attribute\nexport interface MetaAttribute {\n _type: 'metaAttribute'\n key: string\n type: 'string' | 'image'\n value?: string\n image?: SanityImage\n}\n\n// Open Graph settings\nexport interface OpenGraphSettings {\n _type: 'openGraph'\n title?: string\n description?: string\n siteName?: string\n type?: 'website' | 'article' | 'profile' | 'book' | 'music' | 'video' | 'product'\n imageType?: 'upload' | 'url'\n image?: SanityImage\n imageUrl?: string\n}\n\n// Twitter Card settings\nexport interface TwitterCardSettings {\n _type: 'twitter'\n card?: 'summary' | 'summary_large_image' | 'app' | 'player'\n site?: string\n title?: string\n description?: string\n image?: SanityImageWithAlt\n}\n\n// Main SEO Fields interface\nexport interface SeoFields {\n _type: 'seoFields'\n robots?: RobotsSettings\n preview?: string\n title?: string\n description?: string\n metaImage?: SanityImage\n keywords?: string[]\n canonicalUrl?: string\n openGraph?: OpenGraphSettings\n twitter?: TwitterCardSettings\n}\n\n// Type guards\nexport const isSeoFields = (obj: any): obj is SeoFields => {\n return obj && obj._type === 'seoFields'\n}\n\nexport const isOpenGraphSettings = (obj: any): obj is OpenGraphSettings => {\n return obj && obj._type === 'openGraph'\n}\n\nexport const isTwitterCardSettings = (obj: any): obj is TwitterCardSettings => {\n return obj && obj._type === 'twitter'\n}\n\nexport const isMetaAttribute = (obj: any): obj is MetaAttribute => {\n return obj && obj._type === 'metaAttribute'\n}\n\n// Utility types for form validation\nexport interface SeoValidationRules {\n title: {\n maxLength: number\n warningLength: number\n }\n description: {\n maxLength: number\n warningLength: number\n }\n openGraphTitle: {\n maxLength: number\n }\n openGraphDescription: {\n maxLength: number\n }\n twitterTitle: {\n maxLength: number\n }\n twitterDescription: {\n maxLength: number\n }\n}\n\nexport const defaultSeoValidationRules: SeoValidationRules = {\n title: {\n maxLength: 70,\n warningLength: 60,\n },\n description: {\n maxLength: 160,\n warningLength: 150,\n },\n openGraphTitle: {\n maxLength: 95,\n },\n openGraphDescription: {\n maxLength: 200,\n },\n twitterTitle: {\n maxLength: 70,\n },\n twitterDescription: {\n maxLength: 200,\n },\n}\n\n// All types are already exported above with individual export statements\n"],"names":["seoFields"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-seofields",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A Sanity Studio plugin to manage SEO fields like meta titles, descriptions, and Open Graph tags for structured, search-optimized content.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Import the plugin
|
|
2
2
|
import seofields from './plugin'
|
|
3
|
-
export * from './plugin'
|
|
4
3
|
|
|
5
|
-
//
|
|
4
|
+
// Default export the plugin
|
|
6
5
|
export default seofields
|
|
7
6
|
|
|
7
|
+
// Re-export everything from plugin.ts
|
|
8
|
+
export * from './plugin'
|
|
9
|
+
|
|
8
10
|
// Export all TypeScript types
|
|
9
11
|
export * from './types'
|
|
10
12
|
|
package/src/plugin.ts
CHANGED
|
@@ -2,17 +2,23 @@
|
|
|
2
2
|
import {definePlugin} from 'sanity'
|
|
3
3
|
import types from './schemas/types'
|
|
4
4
|
|
|
5
|
-
export interface
|
|
5
|
+
export interface SeoFieldConfig {
|
|
6
6
|
title?: string
|
|
7
7
|
description?: string
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export type SeoFieldKeys =
|
|
10
|
+
export type SeoFieldKeys =
|
|
11
|
+
| 'title'
|
|
12
|
+
| 'description'
|
|
13
|
+
| 'canonicalUrl'
|
|
14
|
+
| 'metaImage'
|
|
15
|
+
| 'keywords'
|
|
16
|
+
| 'metaAttributes'
|
|
11
17
|
|
|
12
18
|
export interface SeoFieldsPluginConfig {
|
|
13
19
|
seoPreview?: boolean
|
|
14
20
|
fieldOverrides?: {
|
|
15
|
-
[key in SeoFieldKeys]?:
|
|
21
|
+
[key in SeoFieldKeys]?: SeoFieldConfig
|
|
16
22
|
}
|
|
17
23
|
}
|
|
18
24
|
|
package/src/schemas/index.ts
CHANGED
|
@@ -67,11 +67,12 @@ export default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}) {
|
|
|
67
67
|
}),
|
|
68
68
|
defineField({
|
|
69
69
|
name: 'metaAttributes',
|
|
70
|
-
title: 'Additional Meta Attributes',
|
|
70
|
+
// title: 'Additional Meta Attributes',
|
|
71
|
+
...getFieldInfo('metaAttributes', config.fieldOverrides),
|
|
71
72
|
type: 'array',
|
|
72
73
|
of: [{type: 'metaAttribute'}],
|
|
73
|
-
description:
|
|
74
|
-
|
|
74
|
+
// description:
|
|
75
|
+
// 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
|
|
75
76
|
}),
|
|
76
77
|
defineField({
|
|
77
78
|
name: 'keywords',
|
package/src/utils/fieldsUtils.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// import OgTitle from '../../../components/openGraph/OgTitle'
|
|
3
3
|
// import OgDescription from '../../../components/openGraph/OgDescription'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {SeoFieldsPluginConfig} from '../plugin'
|
|
6
6
|
|
|
7
7
|
// export default defineType({
|
|
8
8
|
// name: 'openGraph',
|