sanity-plugin-seofields 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +440 -29
- package/dist/SchemaOrgJsonLdPreview-DJLkIPq4.d.cts +78 -0
- package/dist/SchemaOrgJsonLdPreview-DJLkIPq4.d.ts +78 -0
- package/dist/SeoHealthTool-2COI27KI.cjs +8 -0
- package/dist/SeoHealthTool-2COI27KI.cjs.map +1 -0
- package/dist/SeoHealthTool-CWI3KB2V.js +8 -0
- package/dist/SeoHealthTool-CWI3KB2V.js.map +1 -0
- package/dist/{SeoPreview-WYH7NYNM.cjs → SeoPreview-LMZAWWOE.cjs} +35 -38
- package/dist/SeoPreview-LMZAWWOE.cjs.map +1 -0
- package/dist/{SeoPreview-KEGGQSPJ.js → SeoPreview-UNQBKTQO.js} +14 -11
- package/dist/SeoPreview-UNQBKTQO.js.map +1 -0
- package/dist/chunk-3OK3S2F7.cjs +673 -0
- package/dist/chunk-3OK3S2F7.cjs.map +1 -0
- package/dist/{chunk-GWPOJSGW.cjs → chunk-5XTQRILL.cjs} +1022 -307
- package/dist/chunk-5XTQRILL.cjs.map +1 -0
- package/dist/{chunk-2NMEKWO5.js → chunk-7N4MLTMR.js} +8 -15
- package/dist/chunk-7N4MLTMR.js.map +1 -0
- package/dist/{chunk-S367Y35J.cjs → chunk-A57XNQC7.cjs} +9 -20
- package/dist/chunk-A57XNQC7.cjs.map +1 -0
- package/dist/chunk-BWLJDK5J.js +624 -0
- package/dist/chunk-BWLJDK5J.js.map +1 -0
- package/dist/chunk-FUUWBQE2.cjs +195 -0
- package/dist/chunk-FUUWBQE2.cjs.map +1 -0
- package/dist/{chunk-ZBHLMQTS.cjs → chunk-HHO2AKAP.js} +44 -17
- package/dist/chunk-HHO2AKAP.js.map +1 -0
- package/dist/chunk-KIEO6QG3.js +195 -0
- package/dist/chunk-KIEO6QG3.js.map +1 -0
- package/dist/chunk-R2U7JF7U.cjs +624 -0
- package/dist/chunk-R2U7JF7U.cjs.map +1 -0
- package/dist/{chunk-HRKRV2X7.js → chunk-XZKQWV2H.js} +974 -148
- package/dist/chunk-XZKQWV2H.js.map +1 -0
- package/dist/{chunk-HDZZQCH7.js → chunk-Y46ACXM4.cjs} +45 -5
- package/dist/chunk-Y46ACXM4.cjs.map +1 -0
- package/dist/chunk-Z5AOUU4H.js +673 -0
- package/dist/chunk-Z5AOUU4H.js.map +1 -0
- package/dist/cli.js +27 -27
- package/dist/{component-DQ4KuQQD.d.ts → component-CjT1hvxh.d.ts} +3 -27
- package/dist/{component-D1sBm6ts.d.cts → component-WYh0z8as.d.cts} +3 -27
- package/dist/define-cli.cjs +2 -4
- package/dist/define-cli.cjs.map +1 -1
- package/dist/define-cli.js +4 -4
- package/dist/define-cli.js.map +1 -1
- package/dist/head.cjs +14 -0
- package/dist/head.cjs.map +1 -0
- package/dist/head.d.cts +254 -0
- package/dist/head.d.ts +254 -0
- package/dist/head.js +14 -0
- package/dist/head.js.map +1 -0
- package/dist/index.cjs +1797 -230
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -598
- package/dist/index.d.ts +11 -598
- package/dist/index.js +1777 -197
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +210 -447
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +8 -200
- package/dist/next.d.ts +8 -200
- package/dist/next.js +200 -124
- package/dist/next.js.map +1 -1
- package/dist/plugin-DQdThyxG.d.ts +449 -0
- package/dist/plugin-DoGeZP79.d.cts +449 -0
- package/dist/schema/next.cjs +173 -319
- package/dist/schema/next.cjs.map +1 -1
- package/dist/schema/next.d.cts +4 -4
- package/dist/schema/next.d.ts +4 -4
- package/dist/schema/next.js +172 -8
- package/dist/schema/next.js.map +1 -1
- package/dist/schema.cjs +390 -489
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +26 -16
- package/dist/schema.d.ts +26 -16
- package/dist/schema.js +236 -13
- package/dist/schema.js.map +1 -1
- package/dist/server.cjs +149 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +102 -0
- package/dist/server.d.ts +102 -0
- package/dist/server.js +149 -0
- package/dist/server.js.map +1 -0
- package/dist/{types-CeBULNFF.d.ts → types-BAdN1RMu.d.ts} +2 -2
- package/dist/{types-_wBLg6RV.d.cts → types-C5i3KMgs.d.cts} +2 -2
- package/dist/{types-9_wbCIi9.d.ts → types-DP-DiW6f.d.cts} +29 -2
- package/dist/{types-Bb49oRBV.d.cts → types-DRLUGLtX.d.ts} +29 -2
- package/dist/{types-B91ena4g.d.cts → types-DxlPXihz.d.cts} +8 -1
- package/dist/{types-B91ena4g.d.ts → types-DxlPXihz.d.ts} +8 -1
- package/package.json +25 -3
- package/dist/SeoHealthDashboard-IP5BJ322.cjs +0 -10
- package/dist/SeoHealthDashboard-IP5BJ322.cjs.map +0 -1
- package/dist/SeoHealthDashboard-PTUEVYZL.js +0 -4
- package/dist/SeoHealthDashboard-PTUEVYZL.js.map +0 -1
- package/dist/SeoHealthTool-CZZRMWQK.cjs +0 -14
- package/dist/SeoHealthTool-CZZRMWQK.cjs.map +0 -1
- package/dist/SeoHealthTool-IULT4G25.js +0 -12
- package/dist/SeoHealthTool-IULT4G25.js.map +0 -1
- package/dist/SeoPreview-KEGGQSPJ.js.map +0 -1
- package/dist/SeoPreview-WYH7NYNM.cjs.map +0 -1
- package/dist/chunk-2NMEKWO5.js.map +0 -1
- package/dist/chunk-5YRH47VQ.cjs +0 -2167
- package/dist/chunk-5YRH47VQ.cjs.map +0 -1
- package/dist/chunk-ARELX3GK.cjs +0 -689
- package/dist/chunk-ARELX3GK.cjs.map +0 -1
- package/dist/chunk-AW7OKBQQ.js +0 -2161
- package/dist/chunk-AW7OKBQQ.js.map +0 -1
- package/dist/chunk-FONJYVF2.js +0 -610
- package/dist/chunk-FONJYVF2.js.map +0 -1
- package/dist/chunk-GWPOJSGW.cjs.map +0 -1
- package/dist/chunk-HDZZQCH7.js.map +0 -1
- package/dist/chunk-HRKRV2X7.js.map +0 -1
- package/dist/chunk-S367Y35J.cjs.map +0 -1
- package/dist/chunk-ZBHLMQTS.cjs.map +0 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/meta/MetaDescription.tsx","../src/components/meta/MetaImage.tsx","../src/components/meta/MetaTitle.tsx","../src/utils/fieldsUtils.ts","../src/utils/utils.ts","../src/components/openGraph/OgDescription.tsx","../src/components/openGraph/OgImage.tsx","../src/components/openGraph/OgImageUrl.tsx","../src/components/openGraph/OgTitle.tsx","../src/schemas/types/openGraph/index.ts","../src/components/twitter/twitterDescription.tsx","../src/components/twitter/TwitterImage.tsx","../src/components/twitter/TwitterImageUrl.tsx","../src/components/twitter/twitterTitle.tsx","../src/schemas/types/twitter/index.ts","../src/schemas/index.ts","../src/schemas/types/baseMeta/index.ts","../src/schemas/types/metaAttribute/index.ts","../src/schemas/types/metaTag/index.ts","../src/schemas/types/robots/index.ts","../src/schemas/types/index.ts","../src/plugin.ts","../../npm-pro/src/components/SeoHealthPane.tsx","../src/index.ts"],"names":["useFormValue","useMemo","getMetaDescriptionValidationMessages","jsxs","Stack","jsx","Text","getMetaImageValidation","useClient","useState","useEffect","getMetaTitleValidationMessages","getOgDescriptionValidation","getOgImageValidation","getOgImageUrlValidation","getOgTitleValidation","defineType","defineField","__spreadProps","__spreadValues","getTwitterDescriptionValidation","getTwitterImageValidation","getTwitterImageUrlValidation","getTwitterTitleValidation","lazy","createElement","Suspense","_a","definePlugin","__objRest"],"mappings":"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","file":"index.cjs","sourcesContent":["import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getMetaDescriptionValidationMessages} from '../../utils/seoUtils'\n\nconst MetaDescription = (props: StringInputProps): ReactElement => {\n const {value, 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 = useMemo(() => parent?.keywords || [], [parent?.keywords])\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: FeedbackType) => (\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 {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {ObjectInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getMetaImageValidation} from '../../utils/seoUtils'\n\nconst MetaImage = (props: ObjectInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n const hasImage = !!(value as Record<string, unknown> | undefined)?.asset\n\n const feedbackItems = useMemo(\n () => getMetaImageValidation(hasImage, seoParent),\n [hasImage, seoParent],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 MetaImage\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useEffect, useMemo, useState} from 'react'\nimport {StringInputProps, useClient, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getMetaTitleValidationMessages} from '../../utils/seoUtils'\n\nconst MetaTitle = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path, schemaType} = props\n const {options} = schemaType as {\n options?: {\n apiVersion?: string\n titleSuffix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffixQuery?: string\n }\n }\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const rootDoc = (useFormValue([]) as ({_type?: string} & Record<string, unknown>) | null) ?? {}\n const client = useClient({apiVersion: options?.apiVersion ?? '2024-01-01'})\n const [groqTitleSuffix, setGroqTitleSuffix] = useState<string>('')\n\n const titleSuffixQuery = options?.titleSuffixQuery\n const titleSuffixOption = options?.titleSuffix\n\n useEffect(() => {\n if (!titleSuffixQuery) return\n client\n .fetch<string>(titleSuffixQuery)\n .then((result) => {\n setGroqTitleSuffix(result === null || result === undefined ? '' : String(result))\n })\n .catch(() => {\n setGroqTitleSuffix('')\n })\n }, [titleSuffixQuery, client])\n\n const resolvedSuffix = useMemo((): string => {\n if (titleSuffixQuery) return groqTitleSuffix\n if (!titleSuffixOption) return ''\n if (typeof titleSuffixOption === 'function') {\n return titleSuffixOption(rootDoc)\n }\n return titleSuffixOption\n }, [titleSuffixQuery, groqTitleSuffix, titleSuffixOption, rootDoc])\n\n // ` | ` separator = 3 chars\n const suffixLength = resolvedSuffix ? resolvedSuffix.length + 3 : 0\n\n const feedbackItems = useMemo(\n () => getMetaTitleValidationMessages(value || '', keywords, isParentseoField, suffixLength),\n [value, keywords, isParentseoField, suffixLength],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 {AllFieldKeys, SeoFieldsPluginConfig, ValidHiddenFieldKeys} from '../plugin'\n\nconst DEFAULT_FIELD_INFO: Record<AllFieldKeys, {title: string; description: string}> = {\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 openGraphTitle: {\n title: 'Open Graph Title',\n description: '',\n },\n openGraphUrl: {\n title: 'Open Graph URL',\n description:\n 'The canonical URL of the page. This should be the full URL including protocol (https://).',\n },\n openGraphDescription: {\n title: 'Open Graph Description',\n description: '',\n },\n openGraphSiteName: {\n title: 'Open Graph Site Name',\n description: 'The name of your website. This is often the site title.',\n },\n openGraphType: {\n title: 'Open Graph Type',\n description: 'Select the type of content for Open Graph.',\n },\n openGraphImageType: {\n title: 'Image Type',\n description: 'Choose whether to upload an image or provide an image URL for Open Graph.',\n },\n openGraphImage: {\n title: 'Open Graph Image',\n description:\n 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n },\n openGraphImageUrl: {\n title: 'Open Graph Image 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 twitterCard: {\n title: 'X Card Type',\n description: '',\n },\n twitterSite: {\n title: 'Site X Handle',\n description: 'The X (formerly Twitter) handle of the website (e.g., @example)',\n },\n twitterCreator: {\n title: 'X Creator Handle',\n description: 'The X (formerly Twitter) handle of the content creator (e.g., @creator)',\n },\n twitterTitle: {\n title: 'X Title',\n description: 'The title of the content as it should appear on X (formerly Twitter).',\n },\n twitterDescription: {\n title: 'X Description',\n description: 'A brief description of the content for X (formerly Twitter).',\n },\n twitterImageType: {\n title: 'X Image Type',\n description:\n 'Choose whether to upload an image or provide an image URL for X (formerly Twitter).',\n },\n twitterImage: {\n title: 'X Image',\n description:\n 'An image URL which should be at least 120x120px for \"summary\" card and 280x150px for \"summary_large_image\" card.',\n },\n twitterImageUrl: {\n title: 'X Image URL',\n description:\n 'Enter the full URL of the image for X (formerly Twitter). Ensure the image is accessible and meets the recommended size.',\n },\n}\n\nexport const getFieldInfo = (\n fieldName: string,\n fieldOverrides: SeoFieldsPluginConfig['fieldOverrides'],\n): {title: string; description: string} => {\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\n/**\n * Check if a field should be hidden based on the current document type\n */\nexport const isFieldHidden = (\n fieldName: ValidHiddenFieldKeys,\n config: SeoFieldsPluginConfig,\n documentType?: string,\n): boolean => {\n // Check if field is in default hidden fields\n if (config.defaultHiddenFields?.includes(fieldName)) {\n return true\n }\n\n // Check if field is hidden for specific post type\n if (documentType && config.fieldVisibility?.[documentType]?.hiddenFields?.includes(fieldName)) {\n return true\n }\n\n return false\n}\n\n/**\n * Get the hidden function for any field\n */\nexport const getFieldHiddenFunction = (\n fieldName: ValidHiddenFieldKeys,\n config: SeoFieldsPluginConfig,\n) => {\n return ({\n document,\n }: {\n document?: {\n _type?: string\n }\n }): boolean => {\n const documentType = document?._type\n return isFieldHidden(fieldName, config, documentType)\n }\n}\n","export const isEmpty = (value: unknown): boolean => {\n return (\n value === null ||\n value === undefined ||\n (typeof value === 'string' && value.trim() === '') ||\n (Array.isArray(value) && value.length === 0) ||\n (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0)\n )\n}\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getOgDescriptionValidation} from '../../utils/seoUtils'\n\nconst OgDescription = (props: StringInputProps): ReactElement => {\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 = useMemo(() => parent?.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: FeedbackType) => (\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 {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {ObjectInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getOgImageValidation} from '../../utils/seoUtils'\n\nconst OgImage = (props: ObjectInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n const imgValue = value as Record<string, unknown> | undefined\n const hasImage = !!imgValue?.asset\n const altText = imgValue?.alt as string | undefined\n\n const feedbackItems = useMemo(\n () => getOgImageValidation(hasImage, altText, seoParent),\n [hasImage, altText, seoParent],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 OgImage\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getOgImageUrlValidation} from '../../utils/seoUtils'\n\nconst OgImageUrl = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n\n const feedbackItems = useMemo(() => getOgImageUrlValidation(value, seoParent), [value, seoParent])\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 OgImageUrl\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactNode, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getOgTitleValidation} from '../../utils/seoUtils'\n\nconst OgTitle = (props: StringInputProps): ReactNode => {\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 = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const feedbackItems = useMemo(\n () => getOgTitleValidation(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: FeedbackType) => (\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 {defineField, defineType, SchemaTypeDefinition} from 'sanity'\n\nimport OgDescription from '../../../components/openGraph/OgDescription'\nimport OgImage from '../../../components/openGraph/OgImage'\nimport OgImageUrl from '../../../components/openGraph/OgImageUrl'\nimport OgTitle from '../../../components/openGraph/OgTitle'\nimport {SeoFieldsPluginConfig} from '../../../plugin'\nimport {getFieldHiddenFunction, getFieldInfo} from '../../../utils/fieldsUtils'\n\nexport default function openGraph(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {\n return defineType({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'url',\n type: 'url',\n ...getFieldInfo('openGraphUrl', config.fieldOverrides),\n hidden: getFieldHiddenFunction('openGraphUrl', config),\n description:\n 'The canonical URL of the page. This should be the full URL including protocol (https://).',\n }),\n defineField({\n name: 'title',\n ...getFieldInfo('openGraphTitle', config.fieldOverrides),\n type: 'string',\n hidden: getFieldHiddenFunction('openGraphTitle', config),\n components: {\n input: OgTitle, // Can also wrap with a string input + preview\n },\n }),\n defineField({\n name: 'description',\n ...getFieldInfo('openGraphDescription', config.fieldOverrides),\n type: 'text',\n rows: 3,\n hidden: getFieldHiddenFunction('openGraphDescription', config),\n components: {\n input: OgDescription, // Can also wrap with a text area + preview\n },\n }),\n defineField({\n name: 'siteName',\n ...getFieldInfo('openGraphSiteName', config.fieldOverrides),\n type: 'string',\n hidden: getFieldHiddenFunction('openGraphSiteName', config),\n }),\n defineField({\n name: 'type',\n ...getFieldInfo('openGraphType', config.fieldOverrides),\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 hidden: getFieldHiddenFunction('openGraphType', config),\n initialValue: 'website',\n }),\n defineField({\n name: 'imageType',\n ...getFieldInfo('openGraphImageType', config.fieldOverrides),\n type: 'string',\n options: {\n list: [\n {title: 'Upload Image', value: 'upload'},\n {title: 'Image URL', value: 'url'},\n ],\n },\n hidden: getFieldHiddenFunction('openGraphImage', config),\n initialValue: 'upload',\n }),\n defineField({\n name: 'image',\n ...getFieldInfo('openGraphImage', config.fieldOverrides),\n type: 'image',\n options: {\n hotspot: true,\n },\n components: {\n input: OgImage,\n },\n fields: [\n defineField({\n name: 'alt',\n title: 'Image Alt Text',\n type: 'string',\n description: 'A description of the image for accessibility purposes.',\n }),\n ],\n hidden: (context) => {\n const {parent} = context\n if (parent?.imageType !== 'upload') return true\n const hiddenFn = getFieldHiddenFunction('openGraphImage', config)\n return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn\n },\n }),\n defineField({\n name: 'imageUrl',\n ...getFieldInfo('openGraphImageUrl', config.fieldOverrides),\n type: 'url',\n components: {\n input: OgImageUrl,\n },\n hidden: (context) => {\n const {parent} = context\n if (parent?.imageType !== 'url') return true\n const hiddenFn = getFieldHiddenFunction('openGraphImage', config)\n return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn\n },\n }),\n ],\n })\n}\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getTwitterDescriptionValidation} from '../../utils/seoUtils'\n\nconst TwitterDescription = (props: StringInputProps): ReactElement => {\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 = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const feedbackItems = useMemo(\n () => getTwitterDescriptionValidation(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: FeedbackType) => (\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 {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {ObjectInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getTwitterImageValidation} from '../../utils/seoUtils'\n\nconst TwitterImage = (props: ObjectInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n const imgValue = value as Record<string, unknown> | undefined\n const hasImage = !!imgValue?.asset\n const altText = imgValue?.alt as string | undefined\n\n const feedbackItems = useMemo(\n () => getTwitterImageValidation(hasImage, altText, seoParent),\n [hasImage, altText, seoParent],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 TwitterImage\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getTwitterImageUrlValidation} from '../../utils/seoUtils'\n\nconst TwitterImageUrl = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n\n const feedbackItems = useMemo(\n () => getTwitterImageUrlValidation(value, seoParent),\n [value, seoParent],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 TwitterImageUrl\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getTwitterTitleValidation} from '../../utils/seoUtils'\n\nconst TwitterTitle = (props: StringInputProps): ReactElement => {\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 = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const feedbackItems = useMemo(\n () => getTwitterTitleValidation(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: FeedbackType) => (\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 {defineField, defineType, SchemaTypeDefinition} from 'sanity'\n\nimport TwitterDescription from '../../../components/twitter/twitterDescription'\nimport TwitterImage from '../../../components/twitter/TwitterImage'\nimport TwitterImageUrl from '../../../components/twitter/TwitterImageUrl'\nimport TwitterTitle from '../../../components/twitter/twitterTitle'\nimport {SeoFieldsPluginConfig} from '../../../plugin'\nimport {getFieldHiddenFunction, getFieldInfo} from '../../../utils/fieldsUtils'\n\nexport default function twitter(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {\n return defineType({\n name: 'twitter',\n title: 'X (Formerly Twitter)',\n type: 'object',\n fields: [\n defineField({\n name: 'card',\n ...getFieldInfo('twitterCard', config.fieldOverrides),\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 hidden: getFieldHiddenFunction('twitterCard', config),\n initialValue: 'summary_large_image', // good default\n }),\n defineField({\n name: 'site',\n ...getFieldInfo('twitterSite', config.fieldOverrides),\n type: 'string',\n hidden: getFieldHiddenFunction('twitterSite', config),\n }),\n defineField({\n name: 'creator',\n type: 'string',\n ...getFieldInfo('twitterCreator', config.fieldOverrides),\n hidden: getFieldHiddenFunction('twitterCreator', config),\n }),\n defineField({\n name: 'title',\n type: 'string',\n ...getFieldInfo('twitterTitle', config.fieldOverrides),\n hidden: getFieldHiddenFunction('twitterTitle', config),\n components: {\n input: TwitterTitle,\n },\n }),\n defineField({\n name: 'description',\n type: 'text',\n rows: 3,\n ...getFieldInfo('twitterDescription', config.fieldOverrides),\n hidden: getFieldHiddenFunction('twitterDescription', config),\n components: {\n input: TwitterDescription,\n },\n }),\n defineField({\n name: 'imageType',\n ...getFieldInfo('twitterImageType', config.fieldOverrides),\n type: 'string',\n options: {\n list: [\n {title: 'Upload Image', value: 'upload'},\n {title: 'Image URL', value: 'url'},\n ],\n },\n hidden: getFieldHiddenFunction('twitterImage', config),\n initialValue: 'upload',\n }),\n defineField({\n name: 'image',\n ...getFieldInfo('twitterImage', config.fieldOverrides),\n type: 'image',\n options: {\n hotspot: true,\n },\n components: {\n input: TwitterImage,\n },\n fields: [\n defineField({\n name: 'alt',\n title: 'Image Alt Text',\n type: 'string',\n description: 'A description of the image for accessibility purposes.',\n }),\n ],\n hidden: (context) => {\n const {parent} = context\n if (parent?.imageType !== 'upload') return true\n const hiddenFn = getFieldHiddenFunction('twitterImage', config)\n return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn\n },\n }),\n defineField({\n name: 'imageUrl',\n ...getFieldInfo('twitterImageUrl', config.fieldOverrides),\n type: 'url',\n components: {\n input: TwitterImageUrl,\n },\n hidden: (context) => {\n const {parent} = context\n if (parent?.imageType !== 'url') return true\n const hiddenFn = getFieldHiddenFunction('twitterImage', config)\n return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn\n },\n }),\n ],\n })\n}\n","import {createElement, lazy, Suspense} from 'react'\nimport {\n defineField,\n defineType,\n FieldDefinition,\n FieldGroupDefinition,\n SchemaTypeDefinition,\n StringInputProps,\n} from 'sanity'\n\nimport MetaDescription from '../components/meta/MetaDescription'\nimport MetaImage from '../components/meta/MetaImage'\nimport MetaTitle from '../components/meta/MetaTitle'\nimport type {SeoFieldGroup, SeoFieldsPluginConfig, SeoObjectFieldName} from '../plugin'\nimport {getFieldHiddenFunction, getFieldInfo} from '../utils/fieldsUtils'\nimport {isEmpty} from '../utils/utils'\nimport openGraph from './types/openGraph'\nimport twitter from './types/twitter'\n\n// Lazy-load SeoPreview to avoid styled-components breaking `sanity schema extract`\nconst LazySeoPreview = lazy(() => import('../components/SeoPreview'))\nconst SeoPreviewWrapper = (props: StringInputProps) =>\n createElement(Suspense, {fallback: null}, createElement(LazySeoPreview, props))\n\n/**\n * Build a field-name → group-name(s) lookup from the plugin config.\n * A field can appear in multiple groups (Sanity supports `group: string[]`).\n */\nfunction buildFieldGroupMap(groups: SeoFieldGroup[]): Map<SeoObjectFieldName, string[]> {\n const map = new Map<SeoObjectFieldName, string[]>()\n for (const g of groups) {\n for (const fieldName of g.fields) {\n const existing = map.get(fieldName)\n if (existing) {\n existing.push(g.name)\n } else {\n map.set(fieldName, [g.name])\n }\n }\n }\n return map\n}\n\n/**\n * Convert plugin `SeoFieldGroup[]` into Sanity `FieldGroupDefinition[]`.\n */\nfunction toSanityGroups(groups: SeoFieldGroup[]): FieldGroupDefinition[] {\n return groups.map((g) => {\n const def: FieldGroupDefinition = {\n name: g.name,\n title: g.title,\n default: g.default,\n }\n if (g.icon && typeof g.icon !== 'string') {\n def.icon = g.icon\n }\n return def\n })\n}\n\n/**\n * Attach the `group` property to a field definition when groups are configured.\n */\nfunction withGroup(\n field: FieldDefinition,\n fieldGroupMap: Map<SeoObjectFieldName, string[]> | undefined,\n): FieldDefinition {\n if (!fieldGroupMap) return field\n const groups = fieldGroupMap.get(field.name as SeoObjectFieldName)\n if (!groups || groups.length === 0) return field\n return {...field, group: groups.length === 1 ? groups[0] : groups} as FieldDefinition\n}\n\nexport default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {\n const groupsCfg = config.fieldGroups\n const fieldGroupMap = groupsCfg?.length ? buildFieldGroupMap(groupsCfg) : undefined\n const sanityGroups = groupsCfg?.length ? toSanityGroups(groupsCfg) : undefined\n\n return defineType({\n name: 'seoFields',\n title: 'SEO Fields',\n type: 'object',\n ...(sanityGroups ? {groups: sanityGroups} : {}),\n fields: [\n withGroup(\n defineField({\n name: 'robots',\n title: 'Robots Settings',\n type: 'robots',\n hidden: getFieldHiddenFunction('robots', config),\n }),\n fieldGroupMap,\n ),\n // 👇 conditionally spread preview field\n ...((typeof config.seoPreview === 'boolean' && config.seoPreview) ||\n (typeof config.seoPreview === 'object' && !isEmpty(config.seoPreview))\n ? [\n withGroup(\n defineField({\n name: 'preview',\n title: 'SEO Preview',\n type: 'string',\n components: {input: SeoPreviewWrapper},\n options: {\n baseUrl: config.baseUrl || 'https://www.example.com',\n ...(config.apiVersion ? {apiVersion: config.apiVersion} : {}),\n ...(typeof config.seoPreview === 'object' &&\n config.seoPreview &&\n config.seoPreview.prefix\n ? {prefix: config.seoPreview.prefix}\n : {}),\n ...(typeof config.seoPreview === 'object' &&\n config.seoPreview &&\n config.seoPreview.titleSuffix\n ? {titleSuffix: config.seoPreview.titleSuffix}\n : {}),\n ...(typeof config.seoPreview === 'object' &&\n config.seoPreview &&\n config.seoPreview.titleSuffixInheritColor\n ? {titleSuffixInheritColor: config.seoPreview.titleSuffixInheritColor}\n : {}),\n ...(typeof config.seoPreview === 'object' &&\n config.seoPreview &&\n config.seoPreview.titleSuffixQuery\n ? {titleSuffixQuery: config.seoPreview.titleSuffixQuery}\n : {}),\n } as Record<string, unknown>,\n initialValue: '' as string,\n readOnly: true,\n }),\n fieldGroupMap,\n ),\n ]\n : []),\n\n withGroup(\n defineField({\n name: 'title',\n ...getFieldInfo('title', config.fieldOverrides),\n type: 'string',\n components: {\n input: MetaTitle,\n },\n options: {\n ...(config.apiVersion ? {apiVersion: config.apiVersion} : {}),\n ...(typeof config.seoPreview === 'object' &&\n config.seoPreview &&\n config.seoPreview.titleSuffix\n ? {titleSuffix: config.seoPreview.titleSuffix}\n : {}),\n ...(typeof config.seoPreview === 'object' &&\n config.seoPreview &&\n config.seoPreview.titleSuffixQuery\n ? {titleSuffixQuery: config.seoPreview.titleSuffixQuery}\n : {}),\n } as Record<string, unknown>,\n hidden: getFieldHiddenFunction('title', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\n defineField({\n name: 'description',\n ...getFieldInfo('description', config.fieldOverrides),\n type: 'text',\n rows: 3,\n components: {\n input: MetaDescription,\n },\n hidden: getFieldHiddenFunction('description', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\n defineField({\n name: 'metaImage',\n ...getFieldInfo('metaImage', config.fieldOverrides),\n type: 'image',\n options: {\n hotspot: true,\n },\n components: {\n input: MetaImage,\n },\n hidden: getFieldHiddenFunction('metaImage', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\n defineField({\n name: 'metaAttributes',\n ...getFieldInfo('metaAttributes', config.fieldOverrides),\n type: 'array',\n of: [{type: 'metaAttribute'}],\n hidden: getFieldHiddenFunction('metaAttributes', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\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 hidden: getFieldHiddenFunction('keywords', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\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 hidden: getFieldHiddenFunction('canonicalUrl', config),\n }),\n fieldGroupMap,\n ),\n withGroup(openGraph(config) as unknown as FieldDefinition, fieldGroupMap),\n withGroup(twitter(config) as unknown as FieldDefinition, fieldGroupMap),\n ],\n })\n}\n","import {defineField, defineType, SchemaTypeDefinition} from 'sanity'\n\nimport MetaDescription from '../../../components/meta/MetaDescription'\nimport MetaImage from '../../../components/meta/MetaImage'\nimport MetaTitle from '../../../components/meta/MetaTitle'\nimport {SeoFieldsPluginConfig} from '../../../plugin'\nimport {getFieldHiddenFunction, getFieldInfo} from '../../../utils/fieldsUtils'\n\nexport default function baseMeta(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {\n return defineType({\n name: 'baseMeta',\n title: 'Basic Meta Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'title',\n ...getFieldInfo('title', config.fieldOverrides),\n type: 'string',\n components: {\n input: MetaTitle,\n },\n hidden: getFieldHiddenFunction('title', config),\n }),\n defineField({\n name: 'description',\n ...getFieldInfo('description', config.fieldOverrides),\n type: 'text',\n rows: 3,\n components: {\n input: MetaDescription,\n },\n hidden: getFieldHiddenFunction('description', config),\n }),\n defineField({\n name: 'metaImage',\n ...getFieldInfo('metaImage', config.fieldOverrides),\n type: 'image',\n options: {\n hotspot: true,\n },\n components: {\n input: MetaImage,\n },\n hidden: getFieldHiddenFunction('metaImage', config),\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 hidden: getFieldHiddenFunction('keywords', config),\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 hidden: getFieldHiddenFunction('canonicalUrl', config),\n }),\n defineField({\n name: 'metaAttributes',\n ...getFieldInfo('metaAttributes', config.fieldOverrides),\n type: 'array',\n of: [{type: 'metaAttribute'}],\n hidden: getFieldHiddenFunction('metaAttributes', config),\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({attributeName, attributeValueString, attributeValueImage}) {\n let subtitle = ''\n if (attributeValueString) {\n subtitle = `Value: ${attributeValueString}`\n } else if (attributeValueImage) {\n subtitle = 'Value: [Image]'\n } else {\n subtitle = 'No Attribute Value'\n }\n return {\n title: attributeName || 'No Attribute Name',\n subtitle: subtitle,\n media: attributeValueImage,\n }\n },\n },\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:\n 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\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 {SchemaTypeDefinition} from 'sanity'\n\nimport {SeoFieldsPluginConfig} from '../../plugin'\nimport seoFields from '..'\nimport baseMeta from './baseMeta'\nimport metaAttribute from './metaAttribute'\nimport metaTag from './metaTag'\nimport openGraph from './openGraph'\nimport robots from './robots'\nimport twitter from './twitter'\n\nexport default function types(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition[] {\n return [\n seoFields(config), // pass config here\n baseMeta(config), // pass config here\n openGraph(config), // pass config here\n twitter(config), // pass config here\n metaAttribute as SchemaTypeDefinition,\n metaTag,\n robots,\n ]\n}\n","// plugin.ts\nimport {type ComponentType, createElement, lazy, Suspense} from 'react'\nimport {definePlugin} from 'sanity'\n\nimport types from './schemas/types'\nimport type {DeprecationWarning, DocumentWithSeoHealth} from './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 | 'robots'\n\nexport type openGraphFieldKeys =\n | 'openGraphUrl'\n | 'openGraphTitle'\n | 'openGraphDescription'\n | 'openGraphSiteName'\n | 'openGraphType'\n | 'openGraphImageType'\n | 'openGraphImage'\n | 'openGraphImageUrl'\n\nexport type twitterFieldKeys =\n | 'twitterCard'\n | 'twitterSite'\n | 'twitterCreator'\n | 'twitterTitle'\n | 'twitterDescription'\n | 'twitterImageType'\n | 'twitterImage'\n | 'twitterImageUrl'\n\nexport type AllFieldKeys = SeoFieldKeys | openGraphFieldKeys | twitterFieldKeys\n\n/**\n * Names of top-level fields inside the `seoFields` object type.\n * Use these when assigning fields to groups in `fieldGroups`.\n */\nexport type SeoObjectFieldName =\n | 'robots'\n | 'preview'\n | 'title'\n | 'description'\n | 'metaImage'\n | 'metaAttributes'\n | 'keywords'\n | 'canonicalUrl'\n | 'openGraph'\n | 'twitter'\n\n/**\n * Defines a single tab/group within the `seoFields` object.\n */\nexport interface SeoFieldGroup {\n /** Unique key for this group (used internally by Sanity). */\n name: string\n /** Human-readable label shown as the tab title. */\n title: string\n /** Whether this tab is selected by default. Only one group should be `true`. */\n default?: boolean\n /**\n * Field names to include in this group.\n * Use the top-level seoFields field names: `'title'`, `'description'`, `'metaImage'`,\n * `'keywords'`, `'canonicalUrl'`, `'metaAttributes'`, `'robots'`, `'preview'`,\n * `'openGraph'`, `'twitter'`.\n */\n fields: SeoObjectFieldName[]\n /** Optional icon displayed next to the tab title. Must be a React component. */\n icon?: ComponentType\n}\n\nexport type ValidHiddenFieldKeys = Exclude<\n AllFieldKeys,\n 'openGraphImageUrl' | 'twitterImageUrl' | 'openGraphImageType' | 'twitterImageType'\n>\n\nexport interface FieldVisibilityConfig {\n hiddenFields?: ValidHiddenFieldKeys[]\n}\n\nexport interface SeoFieldsPluginConfig {\n /**\n * Enable or configure the SEO preview feature.\n * If set to `true`, the SEO preview will be enabled with default settings.\n * If set to an object, you can provide a custom `prefix` function to modify the URL prefix\n * and/or a `titleSuffix` to append text (e.g. a brand name) after the meta title in the preview.\n * The plugin automatically adds a `|` separator — only provide the suffix text itself.\n *\n * Example:\n * ```\n * seoPreview: {\n * prefix: (doc) => `/${doc.slug?.current || 'untitled'}`,\n * titleSuffix: 'Acme Corp',\n * // or dynamically:\n * titleSuffix: (doc) => doc.brandName || 'Acme Corp',\n * }\n * ```\n */\n seoPreview?:\n | boolean\n | {\n prefix?: (doc: {_type?: string} & Record<string, unknown>) => string\n /**\n * A static string or function appended to the meta title in the Live Preview.\n * Useful for showing a brand suffix (e.g. `'Acme Corp'`) that is added\n * via your Next.js title template but not stored in the Sanity field.\n * The plugin automatically prepends a `|` separator so only provide the\n * text itself. The suffix is rendered in a muted style so editors can\n * distinguish it from the typed title. The combined length (including the\n * separator) is checked against the 60-character SERP limit.\n */\n titleSuffix?: string | ((doc: {_type?: string} & Record<string, unknown>) => string)\n /**\n * When `true`, the `titleSuffix` is rendered in the same color and weight\n * as the main title (`#1a0dab`, `fontWeight: 500`) instead of the default\n * muted style (`#70757a`, `fontWeight: 400`).\n *\n * @default false\n */\n titleSuffixInheritColor?: boolean\n /**\n * A GROQ query string whose result is used as the title suffix in the Live Preview.\n * The query runs against your dataset at Studio load time, making it useful for\n * fetching a dynamic value from another document (e.g. a settings singleton).\n * Takes priority over `titleSuffix` when both are provided.\n *\n * @example\n * ```ts\n * titleSuffixQuery: '*[_type == \"siteSettings\"][0].siteName'\n * ```\n */\n titleSuffixQuery?: string\n }\n\n /**\n * A mapping of field keys to their configuration settings.\n * This allows customization of field titles and descriptions.\n * For example, to change the title of the 'title' field:\n */\n fieldOverrides?: Partial<Record<AllFieldKeys, SeoFieldConfig>>\n /**\n * A mapping of document types to field visibility configurations.\n * This allows you to specify which fields should be hidden for specific document types.\n */\n fieldVisibility?: Record<string, FieldVisibilityConfig>\n\n /**\n * A list of fields that should be hidden by default in all document types.\n * This can be overridden by specific document type settings in `fieldVisibility`.\n */\n defaultHiddenFields?: ValidHiddenFieldKeys[]\n\n /**\n * Group the SEO fields into tabbed sections inside the `seoFields` object.\n * When configured, the Studio shows tabs (Sanity groups) so editors can\n * switch between e.g. \"Meta\", \"Open Graph\", and \"Twitter Card\" panels.\n *\n * @example\n * fieldGroups: [\n * { name: 'meta', title: 'Meta', default: true,\n * fields: ['title', 'description', 'metaImage', 'keywords', 'canonicalUrl', 'metaAttributes', 'robots', 'preview'] },\n * { name: 'openGraph', title: 'Open Graph', fields: ['openGraph'] },\n * { name: 'twitter', title: 'Twitter Card', fields: ['twitter'] },\n * ]\n */\n fieldGroups?: SeoFieldGroup[]\n /**\n * The base URL of your website, used for generating full URLs in the SEO preview.\n * Defaults to 'https://www.example.com' if not provided.\n */\n baseUrl?: string\n /**\n * The Sanity API version to use for all plugin clients (SEO Preview, Health Dashboard).\n * Defaults to '2024-01-01'.\n * @example '2024-01-01'\n */\n apiVersion?: string\n /**\n * Enable or configure the SEO Health Dashboard tool.\n * If set to `true`, the dashboard is enabled with all defaults.\n * If set to an object, you can customise the tool and dashboard settings.\n * Defaults to `true`.\n * Example:\n * ```\n * healthDashboard: {\n * toolTitle: 'SEO Overview', // Studio nav tab label\n * content: {\n * icon: '🔍', // Emoji icon shown before the page heading\n * title: 'My SEO Dashboard',// Page heading inside the tool (no emoji)\n * description: 'Track SEO across all documents', // Subtitle under the heading\n * },\n * display: {\n * typeColumn: false, // Hide the document type column (default: true)\n * documentId: false, // Hide the document ID under titles (default: true)\n * },\n * query: {\n * // Option 1 – filter by specific document types\n * types: ['post', 'page'],\n * // Option 2 – provide a full custom GROQ query (takes precedence over `types`)\n * // Must return documents with at least: _id, _type, title, seo, _updatedAt\n * groq: `*[seo != null && defined(slug.current)]{ _id, _type, title, slug, seo, _updatedAt }`,\n * },\n * }\n * ```\n */\n healthDashboard?:\n | boolean\n | {\n tool?: {\n title?: string\n name?: string\n }\n toolTitle?: string\n content?: {\n icon?: string\n title?: string\n description?: string\n /** Text shown while the license key is being verified. Defaults to \"Verifying license…\" */\n loadingLicense?: string\n /** Text shown while documents are being fetched. Defaults to \"Loading documents…\" */\n loadingDocuments?: string\n /** Text shown when the query returns zero results. Defaults to \"No documents found\" */\n noDocuments?: string\n }\n /**\n * Show or hide the document type column in the results table.\n * Defaults to `true`.\n */\n showTypeColumn?: boolean\n /**\n * Show or hide the Sanity document `_id` under each title.\n * Defaults to `true`.\n */\n showDocumentId?: boolean\n query?: {\n /**\n * Limit the dashboard to specific document types.\n * Example: `['post', 'page']`\n */\n types?: string[]\n /**\n * When using `types`, also require the `seo` field to be non-null.\n * Set to `false` to include documents of those types even if `seo` is missing.\n * Defaults to `true`.\n */\n requireSeo?: boolean\n /**\n * Provide a fully custom GROQ query. Takes precedence over `types`.\n * The query must return documents with at least: _id, _type, title, seo, _updatedAt\n */\n groq?: string\n }\n /**\n * The Sanity API version to use for the client (e.g. '2023-01-01').\n * Defaults to '2023-01-01'.\n * @deprecated Use the root-level `apiVersion` option instead.\n * @example\n * // Before (deprecated):\n * healthDashboard: { apiVersion: '2024-01-01' }\n * // After:\n * apiVersion: '2024-01-01'\n */\n apiVersion?: string\n /**\n * License key for the SEO Health Dashboard pro feature.\n * Obtain a license at https://sanity-plugin-seofields.thehardik.in\n */\n licenseKey?: string\n /**\n * Map raw `_type` values to human-readable display labels.\n * Used in both the Type column and the Type filter dropdown.\n * Any type without an entry falls back to the raw `_type` string.\n *\n * @example\n * typeDisplayLabels: { productDrug: 'Products', singleCondition: 'Condition' }\n */\n typeDisplayLabels?: Record<string, string>\n /**\n * Controls how the document type is rendered in the Type column.\n * - `'badge'` (default) — coloured pill\n * - `'text'` — plain text, useful for dense layouts\n */\n typeColumnMode?: 'badge' | 'text'\n /**\n * The document field to use as the display title in the dashboard.\n *\n * - `string` — use this field for every document type (e.g. `'name'`)\n * - `Record<string, string>` — per-type mapping; unmapped types fall back to `title`\n *\n * @example\n * titleField: 'name'\n *\n * @example\n * titleField: { post: 'title', product: 'name', category: 'label' }\n */\n titleField?: string | Record<string, string>\n /**\n * Callback function to render a custom badge next to the document title.\n * Receives the full document and should return badge data or undefined.\n *\n * @example\n * getDocumentBadge: (doc) => {\n * if (doc.services === 'NHS')\n * return { label: 'NHS', bgColor: '#e0f2fe', textColor: '#0369a1' }\n * if (doc.services === 'Private')\n * return { label: 'Private', bgColor: '#fef3c7', textColor: '#92400e' }\n * }\n */\n getDocumentBadge?: (\n doc: DocumentWithSeoHealth & Record<string, unknown>,\n ) => {label: string; bgColor?: string; textColor?: string; fontSize?: string} | undefined\n /**\n * The `name` of the Sanity structure tool that contains the monitored documents.\n * Required when you have multiple structure tools and the documents live in a\n * non-default one. Clicking a title will navigate to\n * `/{basePath}/{structureTool}/intent/edit/…` directly.\n *\n * @example\n * structureTool: 'common'\n */\n structureTool?: string\n /**\n * Enable preview/demo mode to show dummy data.\n * Useful for testing, documentation, or showcasing the dashboard.\n * When enabled, displays realistic sample documents with various SEO scores.\n * Defaults to `false`.\n *\n * @example\n * previewMode: true\n */\n previewMode?: boolean\n /**\n * Export options for the SEO Health Dashboard.\n * Set to `true` (default) to enable both CSV and JSON export,\n * or configure per-format.\n */\n export?: boolean | {enabled?: boolean; formats?: Array<'csv' | 'json'>}\n /**\n * Show compact inline stat pills in the header row instead of the\n * full 6-card stats grid. Useful for saving vertical space.\n * Defaults to `false`.\n */\n compactStats?: boolean\n }\n}\n\ninterface ResolvedDashboardConfig {\n enabled: boolean\n toolTitle: string\n toolName: string\n icon: string | undefined\n title: string | undefined\n description: string | undefined\n showTypeColumn: boolean | undefined\n showDocumentId: boolean | undefined\n queryTypes: string[] | undefined\n queryRequireSeo: boolean | undefined\n queryGroq: string | undefined\n apiVersion: string | undefined\n licenseKey: string | undefined\n typeDisplayLabels: Record<string, string> | undefined\n typeColumnMode: 'badge' | 'text' | undefined\n titleField: string | Record<string, string> | undefined\n getDocumentBadge:\n | ((\n doc: DocumentWithSeoHealth & Record<string, unknown>,\n ) => {label: string; bgColor?: string; textColor?: string; fontSize?: string} | undefined)\n | undefined\n loadingLicense: string | undefined\n loadingDocuments: string | undefined\n noDocuments: string | undefined\n previewMode: boolean | undefined\n structureTool: string | undefined\n exportEnabled: boolean\n exportFormats: Array<'csv' | 'json'>\n compactStats: boolean\n /** @internal — forwarded to the UI banner when deprecated config keys are detected */\n deprecationWarnings: DeprecationWarning[]\n}\n\n// const V132 = 'v1.3.2'\n// const CHANGELOG_V132 = `https://github.com/hardik-143/sanity-plugin-seofields/blob/main/CHANGELOG.md#132--2026-03-23`\n\nconst resolveDashboardConfig = (\n healthDashboard: SeoFieldsPluginConfig['healthDashboard'],\n rootApiVersion?: string,\n): ResolvedDashboardConfig => {\n const cfg = typeof healthDashboard === 'object' ? healthDashboard : undefined\n\n const deprecationWarnings: DeprecationWarning[] = []\n if (cfg?.apiVersion !== undefined) {\n deprecationWarnings.push({\n key: 'healthDashboard.apiVersion → apiVersion',\n version: 'v1.6.1',\n changelogUrl: 'https://github.com/hardik-143/sanity-plugin-seofields/blob/main/CHANGELOG.md',\n })\n console.warn(\n '[sanity-plugin-seofields] \"healthDashboard.apiVersion\" is deprecated. Move it to the root \"apiVersion\" option instead.',\n )\n }\n\n return {\n enabled: healthDashboard !== false,\n toolTitle: cfg?.tool?.title ?? 'SEO Health',\n toolName: cfg?.tool?.name ?? 'seo-health-dashboard',\n icon: cfg?.content?.icon,\n title: cfg?.content?.title,\n description: cfg?.content?.description,\n showTypeColumn: cfg?.showTypeColumn,\n showDocumentId: cfg?.showDocumentId,\n queryTypes: cfg?.query?.types,\n queryRequireSeo: cfg?.query?.requireSeo,\n queryGroq: cfg?.query?.groq,\n apiVersion: cfg?.apiVersion ?? rootApiVersion,\n licenseKey: cfg?.licenseKey,\n typeDisplayLabels: cfg?.typeDisplayLabels,\n typeColumnMode: cfg?.typeColumnMode,\n titleField: cfg?.titleField,\n getDocumentBadge: cfg?.getDocumentBadge,\n loadingLicense: cfg?.content?.loadingLicense,\n loadingDocuments: cfg?.content?.loadingDocuments,\n noDocuments: cfg?.content?.noDocuments,\n previewMode: cfg?.previewMode,\n structureTool: cfg?.structureTool,\n exportEnabled: (() => {\n const exportCfg = cfg?.export\n if (exportCfg === false) return false\n if (typeof exportCfg === 'object') return exportCfg.enabled ?? true\n return true\n })(),\n exportFormats: (() => {\n const exportCfg = cfg?.export\n if (typeof exportCfg === 'object' && exportCfg.formats) return exportCfg.formats\n return ['csv', 'json'] as Array<'csv' | 'json'>\n })(),\n compactStats: cfg?.compactStats ?? false,\n deprecationWarnings,\n }\n}\n\nconst seofields = definePlugin<SeoFieldsPluginConfig | void>((config = {}) => {\n const {healthDashboard = true} = config as SeoFieldsPluginConfig\n const dash = resolveDashboardConfig(healthDashboard, (config as SeoFieldsPluginConfig).apiVersion)\n\n const LazySeoHealthTool = lazy(() => import('./components/SeoHealthTool'))\n\n const BoundSeoHealthTool = () =>\n createElement(\n Suspense,\n {fallback: null},\n createElement(LazySeoHealthTool, {\n icon: dash.icon,\n title: dash.title,\n description: dash.description,\n showTypeColumn: dash.showTypeColumn,\n showDocumentId: dash.showDocumentId,\n queryTypes: dash.queryTypes,\n queryRequireSeo: dash.queryRequireSeo,\n customQuery: dash.queryGroq,\n apiVersion: dash.apiVersion,\n licenseKey: dash.licenseKey,\n typeDisplayLabels: dash.typeDisplayLabels,\n typeColumnMode: dash.typeColumnMode,\n titleField: dash.titleField,\n getDocumentBadge: dash.getDocumentBadge,\n loadingLicense: dash.loadingLicense,\n loadingDocuments: dash.loadingDocuments,\n noDocuments: dash.noDocuments,\n previewMode: dash.previewMode,\n structureTool: dash.structureTool,\n exportEnabled: dash.exportEnabled,\n exportFormats: dash.exportFormats,\n compactStats: dash.compactStats,\n _deprecationWarnings: dash.deprecationWarnings,\n }),\n )\n\n return {\n name: 'sanity-plugin-seofields',\n schema: {\n types: types(config as SeoFieldsPluginConfig),\n },\n ...(dash.enabled && {\n tools: [\n {\n name: dash.toolName,\n title: dash.toolTitle,\n component: BoundSeoHealthTool,\n icon: () => '📊',\n },\n ],\n }),\n }\n})\n\nexport default seofields\n","import {lazy, Suspense} from 'react'\nimport type {ComponentBuilder, StructureBuilder} from 'sanity/structure'\n\nimport type {SeoHealthDashboardProps} from './SeoHealthDashboard'\n\nconst LazySeoHealthDashboard = lazy(() => import('./SeoHealthDashboard'))\n\n/**\n * Options accepted by `createSeoHealthPane`.\n * All props from `SeoHealthDashboardProps` are supported.\n *\n * `licenseKey` is **required** — the dashboard will not render without it.\n */\nexport interface SeoHealthPaneOptions extends Omit<SeoHealthDashboardProps, 'customQuery'> {\n /** Required license key (format: `SEOF-XXXX-XXXX-XXXX`). */\n licenseKey: string\n /**\n * A fully custom GROQ query used to fetch documents for the dashboard.\n * The query must return documents with at least: `_id`, `_type`, `title`, `seo`, `_updatedAt`.\n *\n * Takes precedence over `queryTypes` when both are provided.\n *\n * @example\n * query: `*[_type in [\"post\",\"page\"] && defined(seo)]{ _id, _type, title, slug, seo, _updatedAt }`\n */\n query?: string\n}\n\n// function isStructureBuilder(arg: unknown): arg is StructureBuilder {\n// return (\n// arg !== null &&\n// typeof arg === 'object' &&\n// typeof (arg as StructureBuilder).component === 'function' &&\n// typeof (arg as StructureBuilder).document === 'function'\n// )\n// }\n\n/**\n * Creates a desk-structure pane for the SEO Health Dashboard.\n *\n * Returns a **`ComponentBuilder`** with a built-in `.child()` resolver so that\n * clicking any document row opens the document editor as a split pane to the right.\n *\n * Use it **directly** as the `.child()` value — do **not** wrap it in `S.component()`.\n *\n * ```ts\n * // sanity.config.ts\n * structure: (S) =>\n * S.list().items([\n * S.listItem()\n * .title('SEO Health')\n * .child(\n * createSeoHealthPane(S, {\n * licenseKey: 'SEOF-XXXX-XXXX-XXXX',\n * query: `*[_type == \"post\" && defined(seo)]{ _id, _type, title, slug, seo, _updatedAt }`,\n * })\n * ),\n * ])\n * ```\n */\nexport function createSeoHealthPane(\n optionsOrS: StructureBuilder,\n optionsWhenS: SeoHealthPaneOptions,\n): ComponentBuilder {\n // ── Two-arg form: structure builder passed as first arg ──────────────────\n const S = optionsOrS\n const {query, openInPane = true, title: paneTitle, ...rest} = optionsWhenS ?? {}\n\n const SeoHealthPane = () => (\n <Suspense fallback={null}>\n <LazySeoHealthDashboard\n customQuery={query}\n openInPane={openInPane}\n title={paneTitle}\n {...rest}\n />\n </Suspense>\n )\n SeoHealthPane.displayName = 'SeoHealthPane'\n\n // Wire up the child resolver so ChildLink URLs resolve to the document editor\n return (S.component(SeoHealthPane) as ComponentBuilder)\n .title(paneTitle ?? 'SEO Health')\n .child((docId: string, {params}: {params: Record<string, string | undefined>}) => {\n const builder = S.document().documentId(docId)\n return params?.type ? builder.schemaType(params.type) : builder\n })\n}\n\nexport default createSeoHealthPane\n","// Import the plugin\nimport seofields from './plugin'\n\n// Default export the plugin\nexport default seofields\n\n// Re-export everything from plugin.ts\nexport * from './plugin'\n\n// Export schema types for external use\nexport {default as seoFieldsSchema} from './schemas'\nexport {default as allSchemas} from './schemas/types'\nexport {default as baseMetaSchema} from './schemas/types/baseMeta'\nexport {default as metaAttributeSchema} from './schemas/types/metaAttribute'\nexport {default as metaTagSchema} from './schemas/types/metaTag'\nexport {default as openGraphSchema} from './schemas/types/openGraph'\nexport {default as robotsSchema} from './schemas/types/robots'\nexport {default as twitterSchema} from './schemas/types/twitter'\n\n// Export dashboard factory (lazy — avoids styled-components at import time)\nexport type {SeoHealthPaneOptions} from './components/SeoHealthPane'\nexport {createSeoHealthPane} from './components/SeoHealthPane'\n\n// Export types\nexport type {DocumentWithSeoHealth, SeoHealthMetrics, SeoHealthStatus} from './types'\n"]}
|
|
1
|
+
{"version":3,"sources":["/Users/hardik/GITHUB/sanity-plugin-seofields/npm/dist/index.cjs","../src/plugin.ts","../src/actions/SeoPublishGateAction.tsx","../src/schemas/index.ts","../src/components/geo/GEOChecklist.tsx","../src/components/meta/FocusKeywordInput.tsx","../src/components/ai/SeoGenButton.tsx","../src/utils/extractDocText.ts","../src/components/meta/KeywordsInput.tsx","../src/components/ai/KeywordSuggestions.tsx","../src/components/meta/MetaDescription.tsx","../src/components/meta/MetaImage.tsx","../src/components/meta/MetaTagsPreview.tsx","../src/components/meta/MetaTitle.tsx","../src/utils/fieldsUtils.ts","../src/utils/utils.ts","../src/schemas/types/openGraph/index.ts","../src/components/openGraph/OgDescription.tsx","../src/components/openGraph/OgImage.tsx","../src/components/openGraph/OgImageUrl.tsx","../src/components/openGraph/OgTitle.tsx","../src/schemas/types/twitter/index.ts","../src/components/twitter/twitterDescription.tsx","../src/components/twitter/TwitterImage.tsx","../src/components/twitter/TwitterImageUrl.tsx","../src/components/twitter/twitterTitle.tsx","../src/schemas/types/baseMeta/index.ts","../src/schemas/types/hreflang/index.ts","../src/schemas/types/metaAttribute/index.ts","../src/schemas/types/metaTag/index.ts","../src/schemas/types/robots/index.ts","../src/schemas/types/index.ts","../src/components/SeoHealthPane.tsx","../src/utils/scoring.ts","../src/index.ts"],"names":["_a","useMemo","useFormValue","jsx","Stack","useRef","useWorkspace","useCallback","Button","SparklesIcon","jsxs","PatchEvent","Box","useEffect","useClient","defineField","defineType","Suspense","openGraph"],"mappings":"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","file":"/Users/hardik/GITHUB/sanity-plugin-seofields/npm/dist/index.cjs","sourcesContent":[null,"// plugin.ts\nexport type AiIndustry =\n | 'blog'\n | 'ecommerce'\n | 'healthcare'\n | 'pharmacy'\n | 'saas'\n | 'finance'\n | 'realestate'\n | 'education'\n | 'restaurant'\n | 'travel'\n | 'fitness'\n | 'hospitality'\n | 'nonprofit'\n | 'legal'\n | 'insurance'\n | 'automotive'\n | 'homeServices'\n\nexport interface AiConfig {\n /** AI provider to use for text generation. */\n provider?: 'openai' | 'anthropic' | 'groq' | 'gemini' | 'ollama'\n /**\n * API key for the provider. Not required for ollama or when using a proxy endpoint.\n * Warning: this is bundled into Studio's client-side JS and readable by anyone with Studio\n * access. For production, use `endpoint` instead — see `sanity-plugin-seofields/server`\n * for ready-made proxy handlers (Next.js, Express, Node, Cloudflare Workers, etc.).\n */\n apiKey?: string\n /** Override the provider's default model. */\n model?: string\n /** Override the provider's base URL. Accepts any OpenAI-compatible endpoint (DeepSeek, xAI Grok, Azure OpenAI, etc.). */\n baseUrl?: string\n /**\n * Proxy endpoint — POST { field, content, focusKeyword, keywords, meta } → { result: string }.\n * Key stays server-side. Build one with `sanity-plugin-seofields/server`, e.g.\n * `createNextRouteHandler({ provider: 'openai', apiKey: process.env.OPENAI_API_KEY })`.\n */\n endpoint?: string\n /** Sampling temperature. Defaults to 0.7. */\n temperature?: number\n /**\n * Root document field(s) used as content for the AI prompt. Defaults to 'body'.\n *\n * - `string` — one field, used for every document type\n * - `string[]` — multiple fields in priority order, used for every document type\n * - `Record<string, string | string[]>` — per-document-type field(s); use a `default`\n * key for unlisted types (falls back to `'body'` if omitted)\n *\n * @example\n * content: ['title', 'excerpt', 'body']\n *\n * @example\n * content: {\n * page: ['sections'],\n * news: 'content',\n * work: ['content', 'contentExtended'],\n * author: 'bio',\n * default: 'body',\n * }\n */\n content?: string | string[] | Record<string, string | string[]>\n /** Industry context for prompts. When set, uses domain-specific vocabulary. Omit for generic prompts. */\n industry?: AiIndustry\n /** Enable test mode — uses static pre-written outputs without any API call. Shows test warning in UI. */\n testMode?: boolean\n /** Number of full generation attempts before giving up. Defaults to 2. */\n maxRetries?: number\n /**\n * Controls what is returned when all attempts fail validation:\n * - true: returns the first raw response (before refinement passes)\n * - false / undefined (default): returns the last attempt's output\n */\n keepFirstOnValidationFail?: boolean\n /**\n * Width of the \"Generate with AI\" button.\n * - 'full' (default): stretches to the field's full width.\n * - 'auto': normal button width, left-aligned.\n */\n buttonWidth?: 'full' | 'auto'\n /** @internal — forwarded from root licenseKey at plugin setup time. Do not set manually. */\n _licenseKey?: string\n /** @internal — Sanity project ID used for pro feature validation. Do not set manually. */\n _projectId?: string\n}\n\nimport {type ComponentType, createElement, lazy, Suspense} from 'react'\nimport type {DocumentActionComponent, DocumentActionsContext} from 'sanity'\nimport {definePlugin} from 'sanity'\n\nimport {createSeoPublishGateAction} from './actions/SeoPublishGateAction'\nimport types from './schemas/types'\nimport type {DeprecationWarning, DocumentWithSeoHealth} from './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 | 'robots'\n | 'focusKeyword'\n | 'hreflangs'\n | 'geoChecklist'\n | 'metaTagsPreview'\n\nexport type openGraphFieldKeys =\n | 'openGraphUrl'\n | 'openGraphTitle'\n | 'openGraphDescription'\n | 'openGraphSiteName'\n | 'openGraphType'\n | 'openGraphImageType'\n | 'openGraphImage'\n | 'openGraphImageUrl'\n\nexport type twitterFieldKeys =\n | 'twitterCard'\n | 'twitterSite'\n | 'twitterCreator'\n | 'twitterTitle'\n | 'twitterDescription'\n | 'twitterImageType'\n | 'twitterImage'\n | 'twitterImageUrl'\n\nexport type AllFieldKeys = SeoFieldKeys | openGraphFieldKeys | twitterFieldKeys\n\n/**\n * Names of top-level fields inside the `seoFields` object type.\n * Use these when assigning fields to groups in `fieldGroups`.\n */\nexport type SeoObjectFieldName =\n | 'robots'\n | 'preview'\n | 'title'\n | 'description'\n | 'metaImage'\n | 'metaAttributes'\n | 'keywords'\n | 'canonicalUrl'\n | 'openGraph'\n | 'twitter'\n | 'focusKeyword'\n | 'hreflangs'\n | 'geoChecklist'\n | 'metaTagsPreview'\n\n/**\n * Defines a single tab/group within the `seoFields` object.\n */\nexport interface SeoFieldGroup {\n /** Unique key for this group (used internally by Sanity). */\n name: string\n /** Human-readable label shown as the tab title. */\n title: string\n /** Whether this tab is selected by default. Only one group should be `true`. */\n default?: boolean\n /**\n * Field names to include in this group.\n * Use the top-level seoFields field names: `'title'`, `'description'`, `'metaImage'`,\n * `'keywords'`, `'canonicalUrl'`, `'metaAttributes'`, `'robots'`, `'preview'`,\n * `'openGraph'`, `'twitter'`.\n */\n fields: SeoObjectFieldName[]\n /** Optional icon displayed next to the tab title. Must be a React component. */\n icon?: ComponentType\n}\n\nexport type ValidHiddenFieldKeys = Exclude<\n AllFieldKeys,\n 'openGraphImageUrl' | 'twitterImageUrl' | 'openGraphImageType' | 'twitterImageType'\n>\n\nexport interface FieldVisibilityConfig {\n hiddenFields?: ValidHiddenFieldKeys[]\n}\n\n/**\n * Individual SEO fields that must be non-empty before publishing.\n * Dot-notation for nested fields (e.g. 'openGraph.title').\n */\nexport type PublishGateRequiredField =\n | 'title'\n | 'description'\n | 'metaImage'\n | 'keywords'\n | 'canonicalUrl'\n | 'openGraph.title'\n | 'openGraph.description'\n | 'openGraph.image'\n | 'openGraph.type'\n | 'twitter.title'\n | 'twitter.description'\n | 'twitter.image'\n | 'twitter.card'\n\nexport interface SeoFieldsPluginConfig {\n /**\n * License key for pro features (SEO Health Dashboard, Publish Gate).\n * Obtain at https://sanity-plugin-seofields.thehardik.in\n */\n licenseKey?: string\n /**\n * Enable or configure the SEO preview feature.\n * If set to `true`, the SEO preview will be enabled with default settings.\n * If set to an object, you can provide a custom `prefix` function to modify the URL prefix\n * and/or a `titleSuffix` to append text (e.g. a brand name) after the meta title in the preview.\n * The plugin automatically adds a `|` separator — only provide the suffix text itself.\n *\n * Example:\n * ```\n * seoPreview: {\n * prefix: (doc) => `/${doc.slug?.current || 'untitled'}`,\n * titleSuffix: 'Acme Corp',\n * // or dynamically:\n * titleSuffix: (doc) => doc.brandName || 'Acme Corp',\n * }\n * ```\n */\n seoPreview?:\n | boolean\n | {\n prefix?: (doc: {_type?: string} & Record<string, unknown>) => string\n /**\n * A static string or function appended to the meta title in the Live Preview.\n * Useful for showing a brand suffix (e.g. `'Acme Corp'`) that is added\n * via your Next.js title template but not stored in the Sanity field.\n * The plugin automatically prepends a `|` separator so only provide the\n * text itself. The suffix is rendered in a muted style so editors can\n * distinguish it from the typed title. The combined length (including the\n * separator) is checked against the 60-character SERP limit.\n */\n titleSuffix?: string | ((doc: {_type?: string} & Record<string, unknown>) => string)\n /**\n * When `true`, the `titleSuffix` is rendered in the same color and weight\n * as the main title (`#1a0dab`, `fontWeight: 500`) instead of the default\n * muted style (`#70757a`, `fontWeight: 400`).\n *\n * @default false\n */\n titleSuffixInheritColor?: boolean\n /**\n * A GROQ query string whose result is used as the title suffix in the Live Preview.\n * The query runs against your dataset at Studio load time, making it useful for\n * fetching a dynamic value from another document (e.g. a settings singleton).\n * Takes priority over `titleSuffix` when both are provided.\n *\n * @example\n * ```ts\n * titleSuffixQuery: '*[_type == \"siteSettings\"][0].siteName'\n * ```\n */\n titleSuffixQuery?: string\n }\n\n /**\n * A mapping of field keys to their configuration settings.\n * This allows customization of field titles and descriptions.\n * For example, to change the title of the 'title' field:\n */\n fieldOverrides?: Partial<Record<AllFieldKeys, SeoFieldConfig>>\n /**\n * A mapping of document types to field visibility configurations.\n * This allows you to specify which fields should be hidden for specific document types.\n */\n fieldVisibility?: Record<string, FieldVisibilityConfig>\n\n /**\n * A list of fields that should be hidden by default in all document types.\n * This can be overridden by specific document type settings in `fieldVisibility`.\n */\n defaultHiddenFields?: ValidHiddenFieldKeys[]\n\n /**\n * Group the SEO fields into tabbed sections inside the `seoFields` object.\n * When configured, the Studio shows tabs (Sanity groups) so editors can\n * switch between e.g. \"Meta\", \"Open Graph\", and \"Twitter Card\" panels.\n *\n * @example\n * fieldGroups: [\n * { name: 'meta', title: 'Meta', default: true,\n * fields: ['title', 'description', 'metaImage', 'keywords', 'canonicalUrl', 'metaAttributes', 'robots', 'preview'] },\n * { name: 'openGraph', title: 'Open Graph', fields: ['openGraph'] },\n * { name: 'twitter', title: 'Twitter Card', fields: ['twitter'] },\n * ]\n */\n fieldGroups?: SeoFieldGroup[]\n /**\n * Show the GEO / AI Overview readiness checklist inside seoFields.\n * Defaults to `true`.\n */\n geo?: boolean\n /**\n * Show the Meta Tags HTML preview inside seoFields.\n * Defaults to `true`.\n */\n metaTagsPreview?: boolean\n /**\n * The base URL of your website, used for generating full URLs in the SEO preview.\n * Defaults to 'https://www.example.com' if not provided.\n */\n baseUrl?: string\n /**\n * The Sanity API version to use for all plugin clients (SEO Preview, Health Dashboard).\n * Defaults to '2024-01-01'.\n * @example '2024-01-01'\n */\n apiVersion?: string\n /**\n * Enable or configure the SEO Health Dashboard tool.\n * If set to `true`, the dashboard is enabled with all defaults.\n * If set to an object, you can customise the tool and dashboard settings.\n * Defaults to `true`.\n * Example:\n * ```\n * healthDashboard: {\n * toolTitle: 'SEO Overview', // Studio nav tab label\n * content: {\n * icon: '🔍', // Emoji icon shown before the page heading\n * title: 'My SEO Dashboard',// Page heading inside the tool (no emoji)\n * description: 'Track SEO across all documents', // Subtitle under the heading\n * },\n * display: {\n * typeColumn: false, // Hide the document type column (default: true)\n * documentId: false, // Hide the document ID under titles (default: true)\n * },\n * query: {\n * // Option 1 – filter by specific document types\n * types: ['post', 'page'],\n * // Option 2 – provide a full custom GROQ query (takes precedence over `types`)\n * // Must return documents with at least: _id, _type, title, seo, _updatedAt\n * groq: `*[seo != null && defined(slug.current)]{ _id, _type, title, slug, seo, _updatedAt }`,\n * },\n * }\n * ```\n */\n healthDashboard?:\n | boolean\n | {\n tool?: {\n title?: string\n name?: string\n }\n toolTitle?: string\n content?: {\n icon?: string\n title?: string\n description?: string\n /** Text shown while the license key is being verified. Defaults to \"Verifying license…\" */\n loadingLicense?: string\n /** Text shown while documents are being fetched. Defaults to \"Loading documents…\" */\n loadingDocuments?: string\n /** Text shown when the query returns zero results. Defaults to \"No documents found\" */\n noDocuments?: string\n }\n /**\n * Show or hide the document type column in the results table.\n * Defaults to `true`.\n */\n showTypeColumn?: boolean\n /**\n * Show or hide the Sanity document `_id` under each title.\n * Defaults to `true`.\n */\n showDocumentId?: boolean\n query?: {\n /**\n * Limit the dashboard to specific document types.\n * Example: `['post', 'page']`\n */\n types?: string[]\n /**\n * When using `types`, also require the `seo` field to be non-null.\n * Set to `false` to include documents of those types even if `seo` is missing.\n * Defaults to `true`.\n */\n requireSeo?: boolean\n /**\n * Provide a fully custom GROQ query. Takes precedence over `types`.\n * The query must return documents with at least: _id, _type, title, seo, _updatedAt\n */\n groq?: string\n }\n /**\n * The Sanity API version to use for the client (e.g. '2023-01-01').\n * Defaults to '2023-01-01'.\n * @deprecated Use the root-level `apiVersion` option instead.\n * @example\n * // Before (deprecated):\n * healthDashboard: { apiVersion: '2024-01-01' }\n * // After:\n * apiVersion: '2024-01-01'\n */\n apiVersion?: string\n /**\n * License key for the SEO Health Dashboard pro feature.\n * @deprecated Use the root-level `licenseKey` option instead.\n */\n licenseKey?: string\n /**\n * Map raw `_type` values to human-readable display labels.\n * Used in both the Type column and the Type filter dropdown.\n * Any type without an entry falls back to the raw `_type` string.\n *\n * @example\n * typeDisplayLabels: { productDrug: 'Products', singleCondition: 'Condition' }\n */\n typeDisplayLabels?: Record<string, string>\n /**\n * Controls how the document type is rendered in the Type column.\n * - `'badge'` (default) — coloured pill\n * - `'text'` — plain text, useful for dense layouts\n */\n typeColumnMode?: 'badge' | 'text'\n /**\n * The document field to use as the display title in the dashboard.\n *\n * - `string` — use this field for every document type (e.g. `'name'`)\n * - `Record<string, string>` — per-type mapping; unmapped types fall back to `title`\n *\n * @example\n * titleField: 'name'\n *\n * @example\n * titleField: { post: 'title', product: 'name', category: 'label' }\n */\n titleField?: string | Record<string, string>\n /**\n * Callback function to render a custom badge next to the document title.\n * Receives the full document and should return badge data or undefined.\n *\n * @example\n * getDocumentBadge: (doc) => {\n * if (doc.services === 'NHS')\n * return { label: 'NHS', bgColor: '#e0f2fe', textColor: '#0369a1' }\n * if (doc.services === 'Private')\n * return { label: 'Private', bgColor: '#fef3c7', textColor: '#92400e' }\n * }\n */\n getDocumentBadge?: (\n doc: DocumentWithSeoHealth & Record<string, unknown>,\n ) => {label: string; bgColor?: string; textColor?: string; fontSize?: string} | undefined\n /**\n * The `name` of the Sanity structure tool that contains the monitored documents.\n * Required when you have multiple structure tools and the documents live in a\n * non-default one. Clicking a title will navigate to\n * `/{basePath}/{structureTool}/intent/edit/…` directly.\n *\n * @example\n * structureTool: 'common'\n */\n structureTool?: string\n /**\n * Enable preview/demo mode to show dummy data.\n * Useful for testing, documentation, or showcasing the dashboard.\n * When enabled, displays realistic sample documents with various SEO scores.\n * Defaults to `false`.\n *\n * @example\n * previewMode: true\n */\n previewMode?: boolean\n /**\n * Export options for the SEO Health Dashboard.\n * Set to `true` (default) to enable both CSV and JSON export,\n * or configure per-format.\n */\n export?: boolean | {enabled?: boolean; formats?: Array<'csv' | 'json'>}\n /**\n * Show compact inline stat pills in the header row instead of the\n * full 6-card stats grid. Useful for saving vertical space.\n * Defaults to `false`.\n */\n compactStats?: boolean\n }\n\n /**\n * Gate publishing based on SEO score or required field presence.\n * Requires a valid root-level `licenseKey` — silently disabled if key is absent or invalid.\n *\n * @example\n * ```ts\n * publishGate: {\n * mode: 'warn',\n * minScore: 70,\n * requiredFields: ['title', 'description', 'openGraph.title'],\n * types: ['post', 'page'],\n * }\n * ```\n */\n /**\n * AI text generation configuration.\n * Supports OpenAI, Anthropic, Groq, Gemini, Ollama, or a custom proxy endpoint.\n * Set `testMode: true` to preview generation without an API key.\n */\n ai?: AiConfig\n\n publishGate?: {\n /**\n * 'block' — disables the Publish button with a tooltip reason (default).\n * 'warn' — shows a confirm dialog; editor can bypass and publish anyway.\n * @default 'block'\n */\n mode?: 'block' | 'warn'\n /**\n * Minimum overall SEO health score (0–100) required to publish.\n * Gate fires when the document's score is below this threshold.\n * @default 60\n */\n minScore?: number\n /**\n * Individual fields that MUST be non-empty. Gate fires if any are missing.\n * Use dot-notation for nested fields: 'openGraph.title', 'twitter.image', etc.\n */\n requiredFields?: PublishGateRequiredField[]\n /**\n * Restrict the gate to specific document types.\n * Omit to apply to all document types that have an `seo` field.\n */\n types?: string[]\n /**\n * Custom message shown in the block tooltip or warn dialog.\n * Accepts a string or a function receiving (score, issues[]).\n */\n message?: string | ((score: number, issues: string[]) => string)\n }\n}\n\ninterface ResolvedDashboardConfig {\n enabled: boolean\n toolTitle: string\n toolName: string\n icon: string | undefined\n title: string | undefined\n description: string | undefined\n showTypeColumn: boolean | undefined\n showDocumentId: boolean | undefined\n queryTypes: string[] | undefined\n queryRequireSeo: boolean | undefined\n queryGroq: string | undefined\n apiVersion: string | undefined\n licenseKey: string | undefined\n typeDisplayLabels: Record<string, string> | undefined\n typeColumnMode: 'badge' | 'text' | undefined\n titleField: string | Record<string, string> | undefined\n getDocumentBadge:\n | ((\n doc: DocumentWithSeoHealth & Record<string, unknown>,\n ) => {label: string; bgColor?: string; textColor?: string; fontSize?: string} | undefined)\n | undefined\n loadingLicense: string | undefined\n loadingDocuments: string | undefined\n noDocuments: string | undefined\n previewMode: boolean | undefined\n structureTool: string | undefined\n exportEnabled: boolean\n exportFormats: Array<'csv' | 'json'>\n compactStats: boolean\n /** @internal — forwarded to the UI banner when deprecated config keys are detected */\n deprecationWarnings: DeprecationWarning[]\n}\n\n// const V132 = 'v1.3.2'\n// const CHANGELOG_V132 = `https://github.com/hardik-143/sanity-plugin-seofields/blob/main/CHANGELOG.md#132--2026-03-23`\n\nconst resolveDashboardConfig = (\n healthDashboard: SeoFieldsPluginConfig['healthDashboard'],\n rootApiVersion?: string,\n rootLicenseKey?: string,\n): ResolvedDashboardConfig => {\n const cfg = typeof healthDashboard === 'object' ? healthDashboard : undefined\n\n const deprecationWarnings: DeprecationWarning[] = []\n if (cfg?.apiVersion !== undefined) {\n deprecationWarnings.push({\n key: 'healthDashboard.apiVersion → apiVersion',\n version: 'v1.6.1',\n changelogUrl: 'https://github.com/hardik-143/sanity-plugin-seofields/blob/main/CHANGELOG.md',\n })\n console.warn(\n '[sanity-plugin-seofields] \"healthDashboard.apiVersion\" is deprecated. Move it to the root \"apiVersion\" option instead.',\n )\n }\n if (cfg?.licenseKey !== undefined && !rootLicenseKey) {\n deprecationWarnings.push({\n key: 'healthDashboard.licenseKey → licenseKey',\n version: 'v1.8.0',\n changelogUrl: 'https://github.com/hardik-143/sanity-plugin-seofields/blob/main/CHANGELOG.md',\n })\n console.warn(\n '[sanity-plugin-seofields] \"healthDashboard.licenseKey\" is deprecated. Move it to the root \"licenseKey\" option instead.',\n )\n }\n\n return {\n enabled: healthDashboard !== false,\n toolTitle: cfg?.tool?.title ?? cfg?.toolTitle ?? 'SEO Health',\n toolName: cfg?.tool?.name ?? 'seo-health-dashboard',\n icon: cfg?.content?.icon,\n title: cfg?.content?.title,\n description: cfg?.content?.description,\n showTypeColumn: cfg?.showTypeColumn,\n showDocumentId: cfg?.showDocumentId,\n queryTypes: cfg?.query?.types,\n queryRequireSeo: cfg?.query?.requireSeo,\n queryGroq: cfg?.query?.groq,\n apiVersion: cfg?.apiVersion ?? rootApiVersion,\n licenseKey: rootLicenseKey ?? cfg?.licenseKey,\n typeDisplayLabels: cfg?.typeDisplayLabels,\n typeColumnMode: cfg?.typeColumnMode,\n titleField: cfg?.titleField,\n getDocumentBadge: cfg?.getDocumentBadge,\n loadingLicense: cfg?.content?.loadingLicense,\n loadingDocuments: cfg?.content?.loadingDocuments,\n noDocuments: cfg?.content?.noDocuments,\n previewMode: cfg?.previewMode,\n structureTool: cfg?.structureTool,\n exportEnabled: (() => {\n const exportCfg = cfg?.export\n if (exportCfg === false) return false\n if (typeof exportCfg === 'object') return exportCfg.enabled ?? true\n return true\n })(),\n exportFormats: (() => {\n const exportCfg = cfg?.export\n if (typeof exportCfg === 'object' && exportCfg.formats) return exportCfg.formats\n return ['csv', 'json'] as Array<'csv' | 'json'>\n })(),\n compactStats: cfg?.compactStats ?? false,\n deprecationWarnings,\n }\n}\n\nconst seofields = definePlugin<SeoFieldsPluginConfig | void>((config = {}) => {\n const cfg = config as SeoFieldsPluginConfig\n const {healthDashboard = true, licenseKey, publishGate} = cfg\n const dash = resolveDashboardConfig(healthDashboard, cfg.apiVersion, licenseKey)\n\n const LazySeoHealthTool = lazy(() => import('./components/SeoHealthTool'))\n\n const BoundSeoHealthTool = () =>\n createElement(\n Suspense,\n {fallback: null},\n createElement(LazySeoHealthTool, {\n icon: dash.icon,\n title: dash.title,\n description: dash.description,\n showTypeColumn: dash.showTypeColumn,\n showDocumentId: dash.showDocumentId,\n queryTypes: dash.queryTypes,\n queryRequireSeo: dash.queryRequireSeo,\n customQuery: dash.queryGroq,\n apiVersion: dash.apiVersion,\n licenseKey: dash.licenseKey,\n typeDisplayLabels: dash.typeDisplayLabels,\n typeColumnMode: dash.typeColumnMode,\n titleField: dash.titleField,\n getDocumentBadge: dash.getDocumentBadge,\n loadingLicense: dash.loadingLicense,\n loadingDocuments: dash.loadingDocuments,\n noDocuments: dash.noDocuments,\n previewMode: dash.previewMode,\n structureTool: dash.structureTool,\n exportEnabled: dash.exportEnabled,\n exportFormats: dash.exportFormats,\n compactStats: dash.compactStats,\n _deprecationWarnings: dash.deprecationWarnings,\n }),\n )\n\n return {\n name: 'sanity-plugin-seofields',\n schema: {\n types: types(cfg),\n },\n ...(publishGate &&\n licenseKey && {\n document: {\n actions: (\n prev: DocumentActionComponent[],\n context: DocumentActionsContext,\n ): DocumentActionComponent[] => {\n if (publishGate.types?.length && !publishGate.types.includes(context.schemaType))\n return prev\n return prev.map((Action) =>\n Action.action === 'publish'\n ? createSeoPublishGateAction(Action, {...publishGate, licenseKey})\n : Action,\n )\n },\n },\n }),\n ...(dash.enabled && {\n tools: [\n {\n name: dash.toolName,\n title: dash.toolTitle,\n component: BoundSeoHealthTool,\n icon: () => '📊',\n },\n ],\n }),\n }\n})\n\nexport default seofields\n","export type {PublishGateOptions} from 'seofields-pro'\nexport {createSeoPublishGateAction} from 'seofields-pro'\n","import {createElement, lazy, Suspense} from 'react'\nimport {\n defineField,\n defineType,\n FieldDefinition,\n FieldGroupDefinition,\n SchemaTypeDefinition,\n StringInputProps,\n} from 'sanity'\n\nimport GEOChecklist from '../components/geo/GEOChecklist'\nimport FocusKeywordInput from '../components/meta/FocusKeywordInput'\nimport KeywordsInput from '../components/meta/KeywordsInput'\nimport MetaDescription from '../components/meta/MetaDescription'\nimport MetaImage from '../components/meta/MetaImage'\nimport MetaTagsPreview from '../components/meta/MetaTagsPreview'\nimport MetaTitle from '../components/meta/MetaTitle'\nimport type {SeoFieldGroup, SeoFieldsPluginConfig, SeoObjectFieldName} from '../plugin'\nimport {getFieldHiddenFunction, getFieldInfo, withLicense} from '../utils/fieldsUtils'\nimport {isEmpty} from '../utils/utils'\nimport openGraph from './types/openGraph'\nimport twitter from './types/twitter'\n\n// Lazy-load SeoPreview to avoid styled-components breaking `sanity schema extract`\nconst LazySeoPreview = lazy(() => import('../components/SeoPreview'))\nconst SeoPreviewWrapper = (props: StringInputProps) =>\n createElement(Suspense, {fallback: null}, createElement(LazySeoPreview, props))\n\n/**\n * Build a field-name → group-name(s) lookup from the plugin config.\n * A field can appear in multiple groups (Sanity supports `group: string[]`).\n */\nfunction buildFieldGroupMap(groups: SeoFieldGroup[]): Map<SeoObjectFieldName, string[]> {\n const map = new Map<SeoObjectFieldName, string[]>()\n for (const g of groups) {\n for (const fieldName of g.fields) {\n const existing = map.get(fieldName)\n if (existing) {\n existing.push(g.name)\n } else {\n map.set(fieldName, [g.name])\n }\n }\n }\n return map\n}\n\n/**\n * Convert plugin `SeoFieldGroup[]` into Sanity `FieldGroupDefinition[]`.\n */\nfunction toSanityGroups(groups: SeoFieldGroup[]): FieldGroupDefinition[] {\n return groups.map((g) => {\n const def: FieldGroupDefinition = {\n name: g.name,\n title: g.title,\n default: g.default,\n }\n if (g.icon && typeof g.icon !== 'string') {\n def.icon = g.icon\n }\n return def\n })\n}\n\n/**\n * Attach the `group` property to a field definition when groups are configured.\n */\nfunction withGroup(\n field: FieldDefinition,\n fieldGroupMap: Map<SeoObjectFieldName, string[]> | undefined,\n): FieldDefinition {\n if (!fieldGroupMap) return field\n const groups = fieldGroupMap.get(field.name as SeoObjectFieldName)\n if (!groups || groups.length === 0) return field\n return {...field, group: groups.length === 1 ? groups[0] : groups} as FieldDefinition\n}\n\ntype SeoPreviewConfig = SeoFieldsPluginConfig['seoPreview']\n\nfunction shouldShowPreviewField(seoPreview: SeoPreviewConfig): boolean {\n if (typeof seoPreview === 'boolean') return seoPreview\n if (typeof seoPreview === 'object') return !isEmpty(seoPreview)\n return false\n}\n\n/** Options shared between the `preview` field and the `title` field's suffix behavior. */\nfunction getSeoPreviewSuffixOptions(seoPreview: SeoPreviewConfig): Record<string, unknown> {\n if (typeof seoPreview !== 'object' || !seoPreview) return {}\n const extra: Record<string, unknown> = {}\n if (seoPreview.titleSuffix) extra.titleSuffix = seoPreview.titleSuffix\n if (seoPreview.titleSuffixQuery) extra.titleSuffixQuery = seoPreview.titleSuffixQuery\n return extra\n}\n\nfunction getPreviewFieldOptions(config: SeoFieldsPluginConfig): Record<string, unknown> {\n const seoPreview = config.seoPreview\n const extra: Record<string, unknown> = {}\n if (typeof seoPreview === 'object' && seoPreview) {\n if (seoPreview.prefix) extra.prefix = seoPreview.prefix\n if (seoPreview.titleSuffixInheritColor) {\n extra.titleSuffixInheritColor = seoPreview.titleSuffixInheritColor\n }\n }\n\n return {\n baseUrl: config.baseUrl || 'https://www.example.com',\n ...(config.apiVersion ? {apiVersion: config.apiVersion} : {}),\n ...getSeoPreviewSuffixOptions(seoPreview),\n ...extra,\n }\n}\n\nfunction getAiOption(config: SeoFieldsPluginConfig): Record<string, unknown> {\n return config.ai ? {ai: withLicense(config.ai, config.licenseKey)} : {}\n}\n\nfunction getTitleFieldOptions(config: SeoFieldsPluginConfig): Record<string, unknown> {\n return {\n ...(config.apiVersion ? {apiVersion: config.apiVersion} : {}),\n ...getSeoPreviewSuffixOptions(config.seoPreview),\n ...getAiOption(config),\n }\n}\n\nfunction buildGeoChecklistField(\n config: SeoFieldsPluginConfig,\n fieldGroupMap: Map<SeoObjectFieldName, string[]> | undefined,\n): FieldDefinition[] {\n if (config.geo === false) return []\n return [\n withGroup(\n defineField({\n name: 'geoChecklist',\n ...getFieldInfo('geoChecklist', config.fieldOverrides),\n type: 'string',\n readOnly: true,\n components: {\n input: GEOChecklist,\n },\n options: {\n ...(config.licenseKey ? {licenseKey: config.licenseKey} : {}),\n } as Record<string, unknown>,\n hidden: getFieldHiddenFunction('geoChecklist', config),\n }),\n fieldGroupMap,\n ),\n ]\n}\n\nfunction buildMetaTagsPreviewField(\n config: SeoFieldsPluginConfig,\n fieldGroupMap: Map<SeoObjectFieldName, string[]> | undefined,\n): FieldDefinition[] {\n if (config.metaTagsPreview === false) return []\n return [\n withGroup(\n defineField({\n name: 'metaTagsPreview',\n ...getFieldInfo('metaTagsPreview', config.fieldOverrides),\n type: 'string',\n readOnly: true,\n components: {\n input: MetaTagsPreview,\n },\n hidden: getFieldHiddenFunction('metaTagsPreview', config),\n }),\n fieldGroupMap,\n ),\n ]\n}\n\nexport default function seoFieldsSchema(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {\n const groupsCfg = config.fieldGroups\n const fieldGroupMap = groupsCfg?.length ? buildFieldGroupMap(groupsCfg) : undefined\n const sanityGroups = groupsCfg?.length ? toSanityGroups(groupsCfg) : undefined\n\n return defineType({\n name: 'seoFields',\n title: 'SEO Fields',\n type: 'object',\n ...(sanityGroups ? {groups: sanityGroups} : {}),\n fields: [\n withGroup(\n defineField({\n name: 'robots',\n title: 'Robots Settings',\n type: 'robots',\n hidden: getFieldHiddenFunction('robots', config),\n }),\n fieldGroupMap,\n ),\n // 👇 conditionally spread preview field\n ...(shouldShowPreviewField(config.seoPreview)\n ? [\n withGroup(\n defineField({\n name: 'preview',\n title: 'SEO Preview',\n type: 'string',\n components: {input: SeoPreviewWrapper},\n options: getPreviewFieldOptions(config) as Record<string, unknown>,\n initialValue: '' as string,\n readOnly: true,\n }),\n fieldGroupMap,\n ),\n ]\n : []),\n\n withGroup(\n defineField({\n name: 'title',\n ...getFieldInfo('title', config.fieldOverrides),\n type: 'string',\n components: {\n input: MetaTitle,\n },\n options: getTitleFieldOptions(config) as Record<string, unknown>,\n hidden: getFieldHiddenFunction('title', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\n defineField({\n name: 'description',\n ...getFieldInfo('description', config.fieldOverrides),\n type: 'text',\n rows: 3,\n components: {\n input: MetaDescription,\n },\n options: getAiOption(config) as Record<string, unknown>,\n hidden: getFieldHiddenFunction('description', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\n defineField({\n name: 'metaImage',\n ...getFieldInfo('metaImage', config.fieldOverrides),\n type: 'image',\n options: {\n hotspot: true,\n },\n components: {\n input: MetaImage,\n },\n hidden: getFieldHiddenFunction('metaImage', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\n defineField({\n name: 'metaAttributes',\n ...getFieldInfo('metaAttributes', config.fieldOverrides),\n type: 'array',\n of: [{type: 'metaAttribute'}],\n hidden: getFieldHiddenFunction('metaAttributes', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\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 components: config.ai ? {input: KeywordsInput} : undefined,\n options: getAiOption(config) as Record<string, unknown>,\n hidden: getFieldHiddenFunction('keywords', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\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 hidden: getFieldHiddenFunction('canonicalUrl', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\n defineField({\n name: 'focusKeyword',\n ...getFieldInfo('focusKeyword', config.fieldOverrides),\n type: 'string',\n components: {\n input: FocusKeywordInput,\n },\n options: getAiOption(config) as Record<string, unknown>,\n hidden: getFieldHiddenFunction('focusKeyword', config),\n }),\n fieldGroupMap,\n ),\n withGroup(\n defineField({\n name: 'hreflangs',\n ...getFieldInfo('hreflangs', config.fieldOverrides),\n type: 'array',\n of: [{type: 'hreflangEntry'}],\n description:\n 'Add alternate language/region versions. Include x-default for the fallback URL.',\n hidden: getFieldHiddenFunction('hreflangs', config),\n }),\n fieldGroupMap,\n ),\n ...buildGeoChecklistField(config, fieldGroupMap),\n ...buildMetaTagsPreviewField(config, fieldGroupMap),\n withGroup(openGraph(config) as unknown as FieldDefinition, fieldGroupMap),\n withGroup(twitter(config) as unknown as FieldDefinition, fieldGroupMap),\n ],\n })\n}\n","import {type ReactElement, useMemo, useState} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\nimport {\n buildGEOProChecks,\n GEOChecklistProChecks,\n GEOLockedTeaser,\n type GEOProCheck,\n} from 'seofields-pro'\n\ninterface GEOCheck {\n id: string\n label: string\n pass: boolean\n hint: string\n}\n\ntype SeoFieldsValue = {\n title?: string\n description?: string\n metaImage?: {asset?: {_ref?: string} | null} | null\n openGraph?: {\n title?: string\n description?: string\n image?: {asset?: {_ref?: string} | null} | null\n imageUrl?: string\n } | null\n focusKeyword?: string\n canonicalUrl?: string\n robots?: {noIndex?: boolean} | null\n keywords?: string[]\n} | null\n\ntype RootDocValue = {\n schemaOrg?: Array<{_type?: string}> | {_type?: string} | null\n} | null\n\nconst GEOChecklist = (props: StringInputProps): ReactElement => {\n const {path, schemaType} = props\n const [expanded, setExpanded] = useState(false)\n\n const licenseKey = (schemaType as {options?: {licenseKey?: string}}).options?.licenseKey\n\n const seoFields = useFormValue([path[0]]) as SeoFieldsValue\n const rootDoc = useFormValue([]) as RootDocValue\n\n const freeChecks = useMemo((): GEOCheck[] => {\n const desc = seoFields?.description || ''\n const title = seoFields?.title || ''\n\n return [\n {\n id: 'description_answer_ready',\n label: 'Description is answer-ready (100–160 chars)',\n pass: !!desc && desc.length >= 100 && desc.length <= 160,\n hint: 'Write a concise 100–160 char answer that directly addresses user intent.',\n },\n {\n id: 'not_noindexed',\n label: 'Page is indexable (not noindex)',\n pass: !seoFields?.robots?.noIndex,\n hint: 'Page is set to noindex — AI overviews cannot cite noindexed pages.',\n },\n {\n id: 'title_concise',\n label: 'Title is 30–60 characters',\n pass: !!(title && title.length >= 30 && title.length <= 60),\n hint: 'Keep your title between 30–60 characters for optimal SERP and AI snippet display.',\n },\n {\n id: 'og_image_present',\n label: 'OG or meta image present',\n pass: !!(seoFields?.metaImage?.asset || seoFields?.openGraph?.image?.asset),\n hint: 'Add a meta or OG image for rich results and social previews.',\n },\n {\n id: 'has_keywords',\n label: 'Keywords added',\n pass: !!(\n seoFields?.keywords &&\n seoFields.keywords.filter((k) => k && k.trim().length > 0).length > 0\n ),\n hint: 'Add at least one keyword to improve topical signal for AI classifiers.',\n },\n ]\n }, [seoFields])\n\n const proChecks = useMemo(\n () => (licenseKey ? buildGEOProChecks(seoFields, rootDoc) : null),\n [licenseKey, seoFields, rootDoc],\n )\n\n const freePassing = freeChecks.filter((c) => c.pass).length\n const proPassing = proChecks ? proChecks.filter((c: GEOProCheck) => c.pass).length : 0\n const totalPassing = freePassing + (proChecks ? proPassing : 0)\n const totalChecks = freeChecks.length + (proChecks ? proChecks.length : 0)\n const percentage = totalChecks > 0 ? (totalPassing / totalChecks) * 100 : 0\n\n let scoreColor = '#dc2626'\n let scoreBg = '#fef2f2'\n let scoreText = '#b91c1c'\n if (percentage >= 80) {\n scoreColor = '#16a34a'\n scoreBg = '#f0fdf4'\n scoreText = '#15803d'\n } else if (percentage >= 50) {\n scoreColor = '#ca8a04'\n scoreBg = '#fefce8'\n scoreText = '#a16207'\n }\n\n return (\n <div\n style={{\n borderRadius: 10,\n border: '1px solid #e5e7eb',\n overflow: 'hidden',\n fontFamily: 'inherit',\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: '16px 20px 14px',\n borderBottom: '1px solid #f3f4f6',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n }}\n >\n <div>\n <div style={{fontSize: 13, fontWeight: 600, color: '#111827', marginBottom: 2}}>\n AI Overview Readiness\n </div>\n <div style={{fontSize: 11, color: '#9ca3af'}}>\n Signals that improve visibility in AI-generated overviews\n </div>\n </div>\n <div\n style={{\n backgroundColor: scoreBg,\n borderRadius: 20,\n padding: '4px 12px',\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n flexShrink: 0,\n }}\n >\n <span style={{fontSize: 16, fontWeight: 700, color: scoreColor}}>{totalPassing}</span>\n <span style={{fontSize: 12, color: scoreText, opacity: 0.7}}>/ {totalChecks}</span>\n </div>\n </div>\n\n {/* Progress bar */}\n <div style={{height: 3, backgroundColor: '#f3f4f6'}}>\n <div\n style={{\n height: '100%',\n width: `${percentage}%`,\n backgroundColor: scoreColor,\n transition: 'width 0.4s ease',\n }}\n />\n </div>\n\n {/* Toggle */}\n <div style={{padding: '10px 20px'}}>\n <button\n type=\"button\"\n onClick={() => setExpanded((prev) => !prev)}\n style={{\n background: 'none',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n fontSize: 12,\n color: '#6366f1',\n fontWeight: 500,\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{\n display: 'inline-block',\n transform: expanded ? 'rotate(90deg)' : 'rotate(0deg)',\n transition: 'transform 0.2s ease',\n flexShrink: 0,\n }}\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n {expanded ? 'Hide checks' : 'Show all checks'}\n </button>\n </div>\n\n {expanded && (\n <div>\n {/* Free checks */}\n <div style={{padding: '0 20px 16px'}}>\n <div\n style={{\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: '0.08em',\n color: '#9ca3af',\n marginBottom: 10,\n textTransform: 'uppercase',\n }}\n >\n Core Checks\n </div>\n <div style={{display: 'flex', flexDirection: 'column', gap: 6}}>\n {freeChecks.map((check) => (\n <CheckRow key={check.id} check={check} />\n ))}\n </div>\n </div>\n\n {/* Pro section */}\n <div\n style={{\n margin: '0 12px 12px',\n borderRadius: 8,\n backgroundColor: licenseKey ? '#faf5ff' : '#f9fafb',\n border: `1px solid ${licenseKey ? '#e9d5ff' : '#f3f4f6'}`,\n overflow: 'hidden',\n }}\n >\n {/* Pro header */}\n <div\n style={{\n padding: '10px 16px',\n borderBottom: `1px solid ${licenseKey ? '#e9d5ff' : '#f3f4f6'}`,\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n }}\n >\n <span\n style={{\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: '0.08em',\n color: licenseKey ? '#7c3aed' : '#9ca3af',\n textTransform: 'uppercase',\n }}\n >\n Pro Insights\n </span>\n {!licenseKey && (\n <span\n style={{\n fontSize: 10,\n backgroundColor: '#f3e8ff',\n color: '#7c3aed',\n borderRadius: 4,\n padding: '1px 6px',\n fontWeight: 600,\n }}\n >\n Unlock with licenseKey\n </span>\n )}\n {licenseKey && proChecks && (\n <span\n style={{\n fontSize: 10,\n backgroundColor: '#d8b4fe',\n color: '#5b21b6',\n borderRadius: 4,\n padding: '1px 6px',\n fontWeight: 600,\n }}\n >\n {proPassing} / {proChecks.length} passing\n </span>\n )}\n </div>\n\n {/* Pro rows */}\n <div style={{padding: '10px 16px'}}>\n {proChecks ? <GEOChecklistProChecks checks={proChecks} /> : <GEOLockedTeaser />}\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction CheckRow({check}: {check: GEOCheck}): ReactElement {\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: 10,\n padding: '9px 12px',\n borderRadius: 7,\n backgroundColor: check.pass ? 'transparent' : '#fff8f8',\n border: `1px solid ${check.pass ? 'transparent' : '#fee2e2'}`,\n }}\n >\n <div\n style={{\n width: 20,\n height: 20,\n borderRadius: '50%',\n backgroundColor: check.pass ? '#dcfce7' : '#fee2e2',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n marginTop: 1,\n }}\n >\n <span\n style={{\n fontSize: 10,\n fontWeight: 800,\n color: check.pass ? '#16a34a' : '#dc2626',\n lineHeight: 1,\n }}\n >\n {check.pass ? '✓' : '✗'}\n </span>\n </div>\n\n <div style={{flex: 1, minWidth: 0}}>\n <div\n style={{\n fontSize: 12,\n fontWeight: 500,\n color: check.pass ? '#374151' : '#1f2937',\n lineHeight: 1.4,\n }}\n >\n {check.label}\n </div>\n {!check.pass && (\n <div style={{fontSize: 11, color: '#9ca3af', marginTop: 3, lineHeight: 1.5}}>\n {check.hint}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default GEOChecklist\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {PatchEvent, set, StringInputProps, useFormValue} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {FeedbackType} from '../../types'\nimport {getFocusKeywordPlacement} from '../../utils/seoUtils'\nimport SeoGenButton from '../ai/SeoGenButton'\n\nconst FocusKeywordInput = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path, schemaType, onChange} = props\n const {options} = schemaType as {options?: {ai?: AiConfig}}\n\n const parent = useFormValue([path[0]]) as {\n title?: string\n description?: string\n } | null\n\n const rootDoc = useFormValue([]) as {\n slug?: {current?: string}\n } | null\n\n const feedbackItems = useMemo((): FeedbackType[] => {\n if (!value) return []\n\n const title = parent?.title || ''\n const description = parent?.description || ''\n const slug = rootDoc?.slug?.current || ''\n const placement = getFocusKeywordPlacement(value, title, description)\n\n const items: FeedbackType[] = []\n\n // 1. Title prominence\n if (title) {\n if (placement.atStartOfTitle) {\n items.push({text: 'Keyword at start of title — excellent', color: 'green'})\n } else if (placement.inTitle) {\n items.push({\n text: 'Keyword in title — move it to the start for more prominence',\n color: 'orange',\n })\n } else {\n items.push({text: 'Keyword missing from meta title', color: 'red'})\n }\n }\n\n // 2. Description\n if (description) {\n if (placement.inDescription) {\n items.push({text: 'Keyword in meta description', color: 'green'})\n } else {\n items.push({text: 'Not in description — include it naturally', color: 'orange'})\n }\n }\n\n // 3. Slug (only if slug exists) — needs rootDoc, not shared with scoring\n if (slug) {\n const kwSlug = value.toLowerCase().replace(/\\s+/g, '-')\n if (slug.includes(kwSlug)) {\n items.push({text: 'Keyword in URL slug', color: 'green'})\n } else {\n items.push({\n text: 'Keyword not in URL slug — consider it for new pages',\n color: 'orange',\n })\n }\n }\n\n // 4. Stuffing guard\n if (placement.isStuffed) {\n items.push({\n text: 'Keyword appears too many times in title — avoid stuffing',\n color: 'red',\n })\n }\n\n return items\n }, [value, parent?.title, parent?.description, rootDoc?.slug?.current])\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <SeoGenButton\n field=\"focusKeyword\"\n ai={options?.ai}\n seoFieldPath={String(path[0])}\n onGenerate={(v) => onChange(PatchEvent.from(set(v)))}\n />\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 FocusKeywordInput\n","import {SparklesIcon} from '@sanity/icons'\nimport {Button, Card, Flex, Text} from '@sanity/ui'\nimport {type ReactElement, useCallback, useEffect, useRef, useState} from 'react'\nimport {useFormValue, useWorkspace} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {extractDocText, resolveContentFields} from '../../utils/extractDocText'\nimport {generateSeoText} from '../../utils/seoContentGen'\nimport {type MetaContext, pickTestOutput, type SeoGenField} from '../../utils/seoPrompts'\n\ntype GenState = 'idle' | 'loading' | 'cooldown' | 'error'\n\ninterface Props {\n field: SeoGenField\n ai?: AiConfig\n seoFieldPath: string\n onGenerate: (value: string) => void\n}\n\nexport default function SeoGenButton({\n field,\n ai,\n seoFieldPath,\n onGenerate,\n}: Props): ReactElement | null {\n const [state, setState] = useState<GenState>('idle')\n const [errorMsg, setErrorMsg] = useState<string | null>(null)\n const abortRef = useRef<AbortController | null>(null)\n const cooldownRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const rootDoc = useFormValue([]) as ({_type?: string} & Record<string, unknown>) | null\n const workspace = useWorkspace()\n const seoObj = useFormValue([seoFieldPath]) as Record<string, unknown> | null\n const focusKeyword = (seoObj?.focusKeyword as string | undefined) ?? ''\n const keywords = (seoObj?.keywords as string[] | undefined) ?? []\n const bodyField = resolveContentFields(ai?.content, rootDoc?._type)\n const content = rootDoc ? extractDocText(rootDoc, bodyField) : ''\n\n const meta: MetaContext | undefined =\n field === 'focusKeyword'\n ? {\n title: (seoObj?.title as string | undefined) ?? undefined,\n description: (seoObj?.description as string | undefined) ?? undefined,\n slug: (rootDoc?.slug as {current?: string} | undefined)?.current ?? undefined,\n }\n : undefined\n\n const handleGenerate = useCallback(async () => {\n if (!ai) return\n\n abortRef.current?.abort()\n if (cooldownRef.current) clearTimeout(cooldownRef.current)\n\n setState('loading')\n setErrorMsg(null)\n\n try {\n let result: string\n if (ai.testMode) {\n await new Promise<void>((r) => setTimeout(r, 400))\n result = pickTestOutput(field)\n } else {\n const controller = new AbortController()\n abortRef.current = controller\n result = await generateSeoText(field, content, focusKeyword, keywords, {\n config: {...ai, _projectId: workspace.projectId},\n signal: controller.signal,\n meta,\n })\n }\n\n onGenerate(result)\n setState('cooldown')\n cooldownRef.current = setTimeout(() => setState('idle'), 2000)\n } catch (err: unknown) {\n if ((err as {name?: string}).name === 'AbortError') {\n setState('idle')\n return\n }\n setErrorMsg(err instanceof Error ? err.message : 'Generation failed.')\n setState('error')\n }\n }, [ai, field, content, focusKeyword, keywords, meta, onGenerate, workspace.projectId])\n\n useEffect(() => {\n return () => {\n abortRef.current?.abort()\n if (cooldownRef.current) clearTimeout(cooldownRef.current)\n }\n }, [])\n\n const isConfigured = Boolean(\n ai?.apiKey || ai?.endpoint || ai?.testMode || ai?.provider === 'ollama',\n )\n if (!isConfigured) return null\n\n const isLoading = state === 'loading'\n const isTestMode = Boolean(ai?.testMode)\n let buttonText = 'Generate with AI'\n if (isLoading) buttonText = 'Generating…'\n else if (isTestMode) buttonText = 'Generate (test)'\n\n const isFullWidth = (ai?.buttonWidth ?? 'full') === 'full'\n\n return (\n <Flex direction=\"column\" gap={1} align={isFullWidth ? 'stretch' : 'flex-start'}>\n <Button\n mode=\"ghost\"\n tone=\"primary\"\n paddingX={isFullWidth ? 2 : 3}\n paddingY={3}\n fontSize={1}\n icon={SparklesIcon}\n text={buttonText}\n onClick={handleGenerate}\n disabled={isLoading || state === 'cooldown'}\n />\n {!content && (\n <Text size={0} muted>\n (Set <code>ai.content</code> in plugin config for better results)\n </Text>\n )}\n {state === 'error' && errorMsg && (\n <Card padding={2} tone=\"critical\" radius={2}>\n <Text size={1}>{errorMsg}</Text>\n </Card>\n )}\n </Flex>\n )\n}\n","type PortableTextSpan = {_type?: string; text?: string}\ntype AnyBlock = {_type?: string; children?: PortableTextSpan[]; [key: string]: unknown}\n\nfunction extractBlockText(block: AnyBlock): string {\n // Standard Portable Text block\n if (block._type === 'block' && Array.isArray(block.children)) {\n return (block.children as PortableTextSpan[]).map((s) => s.text ?? '').join('')\n }\n // Wrapper object — recurse into any string or array fields\n const parts: string[] = []\n for (const key of Object.keys(block)) {\n if (key === '_type' || key === '_key') continue\n const val = block[key]\n if (typeof val === 'string' && val.trim()) parts.push(val.trim())\n else if (Array.isArray(val)) parts.push(extractArrayText(val as AnyBlock[]))\n }\n return parts.filter(Boolean).join(' ')\n}\n\nfunction extractArrayText(arr: AnyBlock[]): string {\n return arr\n .map((b) => extractBlockText(b))\n .filter(Boolean)\n .join('\\n')\n}\n\nexport function extractFieldText(value: unknown): string {\n if (typeof value === 'string') return value\n if (!Array.isArray(value)) return ''\n return extractArrayText(value as AnyBlock[])\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n}\n\nexport function extractDocText(doc: Record<string, unknown>, bodyField: string | string[]): string {\n const fields = [bodyField].flat()\n const text = fields\n .map((f) => extractFieldText(doc[f]))\n .filter(Boolean)\n .join('\\n\\n')\n return text.slice(0, 4000)\n}\n\nexport function resolveContentFields(\n content: string | string[] | Record<string, string | string[]> | undefined,\n docType: string | undefined,\n): string | string[] {\n if (!content || typeof content === 'string' || Array.isArray(content)) return content ?? 'body'\n const key = docType && docType in content ? docType : 'default'\n return content[key] ?? 'body'\n}\n","import {Stack} from '@sanity/ui'\nimport {type ReactElement} from 'react'\nimport {ArrayOfPrimitivesInputProps, PatchEvent, set} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport KeywordSuggestions from '../ai/KeywordSuggestions'\n\nconst KeywordsInput = (props: ArrayOfPrimitivesInputProps): ReactElement => {\n const {value, renderDefault, path, schemaType, onChange} = props\n const {options} = schemaType as {options?: {ai?: AiConfig}}\n\n const existing: string[] = (value as string[] | undefined) ?? []\n\n const handleAdd = (kw: string) => {\n if (existing.includes(kw)) return\n onChange(PatchEvent.from(set([...existing, kw])))\n }\n\n const handleAddAll = (keywords: string[]) => {\n const merged = [...new Set([...existing, ...keywords])]\n onChange(PatchEvent.from(set(merged)))\n }\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <KeywordSuggestions\n ai={options?.ai}\n seoFieldPath={String(path[0])}\n existing={existing}\n onAdd={handleAdd}\n onAddAll={handleAddAll}\n />\n </Stack>\n )\n}\n\nexport default KeywordsInput\n","import {SparklesIcon} from '@sanity/icons'\nimport {Badge, Box, Button, Card, Flex, Spinner, Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useCallback, useEffect, useRef, useState} from 'react'\nimport {useFormValue, useWorkspace} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {extractDocText, resolveContentFields} from '../../utils/extractDocText'\nimport {generateSeoText} from '../../utils/seoContentGen'\nimport {pickTestOutput} from '../../utils/seoPrompts'\n\ninterface Props {\n ai?: AiConfig\n seoFieldPath: string\n existing: string[]\n onAdd: (keyword: string) => void\n onAddAll: (keywords: string[]) => void\n}\n\ntype PanelState = 'idle' | 'loading' | 'suggestions' | 'error'\n\nexport default function KeywordSuggestions({\n ai,\n seoFieldPath,\n existing,\n onAdd,\n onAddAll,\n}: Props): ReactElement | null {\n const [panelState, setPanelState] = useState<PanelState>('idle')\n const [suggestions, setSuggestions] = useState<string[]>([])\n const [errorMsg, setErrorMsg] = useState<string | null>(null)\n const abortRef = useRef<AbortController | null>(null)\n\n const rootDoc = useFormValue([]) as ({_type?: string} & Record<string, unknown>) | null\n const workspace = useWorkspace()\n const seoObj = useFormValue([seoFieldPath]) as Record<string, unknown> | null\n const focusKeyword = (seoObj?.focusKeyword as string | undefined) ?? ''\n const bodyField = resolveContentFields(ai?.content, rootDoc?._type)\n const content = rootDoc ? extractDocText(rootDoc, bodyField) : ''\n\n const handleSuggest = useCallback(async () => {\n if (!ai) return\n\n abortRef.current?.abort()\n abortRef.current = new AbortController()\n\n setPanelState('loading')\n setErrorMsg(null)\n setSuggestions([])\n\n try {\n let csv: string\n if (ai.testMode) {\n await new Promise<void>((r) => setTimeout(r, 400))\n csv = pickTestOutput('keywords')\n } else {\n csv = await generateSeoText('keywords', content, focusKeyword, existing, {\n config: {...ai, _projectId: workspace.projectId},\n signal: abortRef.current.signal,\n })\n }\n\n const parsed = csv\n .split(',')\n .map((k) => k.replace(/^[\"']|[\"']$/g, '').trim())\n .filter(Boolean)\n\n if (parsed.length === 0) {\n setErrorMsg('No keywords returned. Try again.')\n setPanelState('error')\n return\n }\n\n setSuggestions(parsed)\n setPanelState('suggestions')\n } catch (err: unknown) {\n if ((err as {name?: string}).name === 'AbortError') {\n setPanelState('idle')\n return\n }\n setErrorMsg(err instanceof Error ? err.message : 'Keyword generation failed.')\n setPanelState('error')\n }\n }, [ai, content, focusKeyword, existing, workspace.projectId])\n\n const handleAdd = useCallback(\n (kw: string) => {\n onAdd(kw)\n setSuggestions((prev) => prev.filter((s) => s !== kw))\n if (suggestions.length <= 1) setPanelState('idle')\n },\n [onAdd, suggestions.length],\n )\n\n const handleAddAll = useCallback(() => {\n const toAdd = suggestions.filter((kw) => !existing.includes(kw))\n if (toAdd.length > 0) onAddAll(toAdd)\n setSuggestions([])\n setPanelState('idle')\n }, [suggestions, existing, onAddAll])\n\n const handleDismiss = useCallback(() => {\n abortRef.current?.abort()\n setSuggestions([])\n setPanelState('idle')\n setErrorMsg(null)\n }, [])\n\n useEffect(() => {\n return () => {\n abortRef.current?.abort()\n }\n }, [])\n\n const isConfigured = Boolean(\n ai?.apiKey || ai?.endpoint || ai?.testMode || ai?.provider === 'ollama',\n )\n if (!isConfigured) return null\n\n if (panelState === 'idle' || panelState === 'error') {\n return (\n <Stack space={2}>\n <Button\n mode=\"ghost\"\n tone=\"primary\"\n paddingX={2}\n paddingY={3}\n fontSize={1}\n icon={SparklesIcon}\n text={ai?.testMode ? 'Suggest Keywords (test)' : 'Suggest Keywords with AI'}\n onClick={handleSuggest}\n />\n {panelState === 'error' && errorMsg && (\n <Card padding={2} tone=\"critical\" radius={2}>\n <Text size={1}>{errorMsg}</Text>\n </Card>\n )}\n </Stack>\n )\n }\n\n if (panelState === 'loading') {\n return (\n <Flex align=\"center\" gap={2} paddingY={2}>\n <Spinner muted />\n <Text size={1} muted>\n Finding keywords…\n </Text>\n </Flex>\n )\n }\n\n const pendingSuggestions = suggestions.filter((kw) => !existing.includes(kw))\n const alreadyAdded = suggestions.filter((kw) => existing.includes(kw))\n\n return (\n <Card padding={3} radius={2} tone=\"transparent\" border>\n <Stack space={3}>\n <Flex align=\"center\" justify=\"space-between\">\n <Text size={1} weight=\"semibold\" muted>\n AI keyword suggestions\n </Text>\n <Flex gap={2}>\n {pendingSuggestions.length > 1 && (\n <Button\n mode=\"ghost\"\n tone=\"primary\"\n paddingX={2}\n paddingY={2}\n fontSize={1}\n text=\"Add all\"\n onClick={handleAddAll}\n />\n )}\n <Button\n mode=\"ghost\"\n tone=\"default\"\n paddingX={2}\n paddingY={2}\n fontSize={1}\n text=\"Dismiss\"\n onClick={handleDismiss}\n />\n </Flex>\n </Flex>\n\n {pendingSuggestions.length > 0 && (\n <Box>\n <Text size={0} muted style={{marginBottom: 8}}>\n Click to add:\n </Text>\n <Flex gap={2} wrap=\"wrap\" style={{marginTop: 6}}>\n {pendingSuggestions.map((kw) => (\n <button\n key={kw}\n type=\"button\"\n onClick={() => handleAdd(kw)}\n style={{\n padding: '4px 10px',\n borderRadius: 20,\n border: '1px solid var(--card-border-color)',\n background: 'transparent',\n color: 'var(--card-fg-color)',\n cursor: 'pointer',\n fontSize: 12,\n lineHeight: 1.4,\n transition: 'background 0.15s',\n }}\n onMouseEnter={(e) => {\n ;(e.currentTarget as HTMLButtonElement).style.background =\n 'var(--card-border-color)'\n }}\n onMouseLeave={(e) => {\n ;(e.currentTarget as HTMLButtonElement).style.background = 'transparent'\n }}\n >\n + {kw}\n </button>\n ))}\n </Flex>\n </Box>\n )}\n\n {alreadyAdded.length > 0 && (\n <Box>\n <Text size={0} muted style={{marginBottom: 6}}>\n Already added:\n </Text>\n <Flex gap={2} wrap=\"wrap\" style={{marginTop: 4}}>\n {alreadyAdded.map((kw) => (\n <Badge key={kw} tone=\"positive\" radius={5} fontSize={0} padding={2}>\n ✓ {kw}\n </Badge>\n ))}\n </Flex>\n </Box>\n )}\n\n {pendingSuggestions.length === 0 && (\n <Text size={1} muted>\n All suggested keywords added.\n </Text>\n )}\n </Stack>\n </Card>\n )\n}\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {PatchEvent, set, StringInputProps, useFormValue} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {FeedbackType} from '../../types'\nimport {analyzeReadability} from '../../utils/readability'\nimport {getMetaDescriptionValidationMessages} from '../../utils/seoUtils'\nimport SeoGenButton from '../ai/SeoGenButton'\n\nconst MetaDescription = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path, schemaType, onChange} = props\n const {options} = schemaType as {options?: {ai?: AiConfig}}\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const feedbackItems = useMemo(\n () => getMetaDescriptionValidationMessages(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n const readability = useMemo(() => analyzeReadability(value || ''), [value])\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <SeoGenButton\n field=\"description\"\n ai={options?.ai}\n seoFieldPath={String(path[0])}\n onGenerate={(v) => onChange(PatchEvent.from(set(v)))}\n />\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 {readability && (\n <div style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: readability.color,\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n Readability: {readability.label} (Flesch {readability.score} · Grade{' '}\n {readability.gradeLevel})\n </Text>\n </div>\n )}\n </Stack>\n )\n}\n\nexport default MetaDescription\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {ObjectInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getMetaImageValidation} from '../../utils/seoUtils'\n\nconst MetaImage = (props: ObjectInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n const hasImage = !!(value as Record<string, unknown> | undefined)?.asset\n\n const feedbackItems = useMemo(\n () => getMetaImageValidation(hasImage, seoParent),\n [hasImage, seoParent],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 MetaImage\n","import {Box, Button, Card, Flex, Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo, useState} from 'react'\nimport {StringInputProps, useClient, useColorScheme, useFormValue} from 'sanity'\n\nimport {tokenizeHtml} from '../../utils/syntaxHighlight'\n\ntype SeoFieldsValue = {\n title?: string\n description?: string\n metaImage?: {asset?: {_ref?: string} | null} | null\n openGraph?: {\n title?: string\n description?: string\n siteName?: string\n type?: string\n url?: string\n image?: {asset?: {_ref?: string} | null} | null\n imageUrl?: string\n } | null\n twitter?: {\n card?: string\n site?: string\n creator?: string\n title?: string\n description?: string\n image?: {asset?: {_ref?: string} | null} | null\n imageUrl?: string\n } | null\n canonicalUrl?: string\n robots?: {\n noIndex?: boolean\n noFollow?: boolean\n noTranslate?: boolean\n noImageIndex?: boolean\n } | null\n keywords?: string[]\n hreflangs?: Array<{locale: string; url: string}>\n metaAttributes?: Array<{\n key?: string\n type?: string\n value?: string\n image?: {asset?: {_ref?: string} | null} | null\n }>\n} | null\n\nfunction resolveImageUrl(\n asset: {_ref?: string} | null | undefined,\n projectId: string,\n dataset: string,\n): string | null {\n const ref = asset?._ref\n if (!ref) return null\n const parts = ref.replace('image-', '').split('-')\n const ext = parts[parts.length - 1]\n const dims = parts[parts.length - 2]\n const id = parts.slice(0, parts.length - 2).join('-')\n return `https://cdn.sanity.io/images/${projectId}/${dataset}/${id}-${dims}.${ext}`\n}\n\nconst MetaTagsPreview = (props: StringInputProps): ReactElement => {\n const {path} = props\n const [expanded, setExpanded] = useState(false)\n const [copyLabel, setCopyLabel] = useState('Copy')\n\n const {scheme} = useColorScheme()\n const seoFields = useFormValue([path[0]]) as SeoFieldsValue\n const client = useClient({apiVersion: '2024-01-01'})\n const {projectId = '', dataset = ''} = client.config()\n\n const rawHtml = useMemo((): string => {\n const lines: string[] = []\n\n lines.push('<meta charset=\"UTF-8\" />')\n lines.push('<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />')\n\n if (seoFields?.title) lines.push(`<title>${seoFields.title}</title>`)\n if (seoFields?.description)\n lines.push(`<meta name=\"description\" content=\"${seoFields.description}\" />`)\n\n if (seoFields?.robots) {\n const parts: string[] = []\n if (seoFields.robots.noIndex) parts.push('noindex')\n if (seoFields.robots.noFollow) parts.push('nofollow')\n if (seoFields.robots.noTranslate) parts.push('notranslate')\n if (seoFields.robots.noImageIndex) parts.push('noimageindex')\n if (parts.length) lines.push(`<meta name=\"robots\" content=\"${parts.join(', ')}\" />`)\n }\n\n if (seoFields?.keywords?.length)\n lines.push(`<meta name=\"keywords\" content=\"${seoFields.keywords.join(', ')}\" />`)\n\n if (seoFields?.canonicalUrl)\n lines.push(`<link rel=\"canonical\" href=\"${seoFields.canonicalUrl}\" />`)\n\n const og = seoFields?.openGraph\n if (og?.type) lines.push(`<meta property=\"og:type\" content=\"${og.type}\" />`)\n if (og?.title) lines.push(`<meta property=\"og:title\" content=\"${og.title}\" />`)\n if (og?.description)\n lines.push(`<meta property=\"og:description\" content=\"${og.description}\" />`)\n if (og?.siteName) lines.push(`<meta property=\"og:site_name\" content=\"${og.siteName}\" />`)\n if (og?.url) lines.push(`<meta property=\"og:url\" content=\"${og.url}\" />`)\n\n const ogImg =\n og?.imageUrl ||\n resolveImageUrl(og?.image?.asset, projectId, dataset) ||\n resolveImageUrl(seoFields?.metaImage?.asset, projectId, dataset)\n if (ogImg) lines.push(`<meta property=\"og:image\" content=\"${ogImg}\" />`)\n\n const tw = seoFields?.twitter\n if (tw?.card) lines.push(`<meta name=\"twitter:card\" content=\"${tw.card}\" />`)\n if (tw?.site) lines.push(`<meta name=\"twitter:site\" content=\"${tw.site}\" />`)\n if (tw?.creator) lines.push(`<meta name=\"twitter:creator\" content=\"${tw.creator}\" />`)\n if (tw?.title) lines.push(`<meta name=\"twitter:title\" content=\"${tw.title}\" />`)\n if (tw?.description)\n lines.push(`<meta name=\"twitter:description\" content=\"${tw.description}\" />`)\n\n const twImg = tw?.imageUrl || resolveImageUrl(tw?.image?.asset, projectId, dataset)\n if (twImg) lines.push(`<meta name=\"twitter:image\" content=\"${twImg}\" />`)\n\n if (seoFields?.hreflangs?.length) {\n for (const e of seoFields.hreflangs) {\n if (e.locale && e.url)\n lines.push(`<link rel=\"alternate\" hreflang=\"${e.locale}\" href=\"${e.url}\" />`)\n }\n }\n\n if (seoFields?.metaAttributes?.length) {\n for (const attr of seoFields.metaAttributes) {\n if (attr.key && attr.type === 'string' && attr.value) {\n lines.push(`<meta name=\"${attr.key}\" content=\"${attr.value}\" />`)\n } else if (attr.key && attr.type === 'image' && attr.image?.asset) {\n const imgUrl = resolveImageUrl(attr.image.asset, projectId, dataset)\n if (imgUrl) lines.push(`<meta name=\"${attr.key}\" content=\"${imgUrl}\" />`)\n }\n }\n }\n\n return lines.join('\\n')\n }, [seoFields, projectId, dataset])\n\n const tagCount = useMemo(() => rawHtml.split('\\n').filter(Boolean).length, [rawHtml])\n\n const tokens = useMemo(() => tokenizeHtml(rawHtml, scheme), [rawHtml, scheme])\n\n const handleCopy = () => {\n navigator.clipboard.writeText(rawHtml).then(() => {\n setCopyLabel('Copied!')\n setTimeout(() => setCopyLabel('Copy'), 2000)\n })\n }\n\n const isDark = scheme === 'dark'\n const codeBg = isDark ? '#161b22' : '#f6f8fa'\n const codeBorder = isDark ? '#30363d' : '#d0d7de'\n const headerBg = isDark ? '#21262d' : '#f0f2f4'\n const headerBorder = isDark ? '#30363d' : '#d0d7de'\n const headerText = isDark ? '#e6edf3' : '#24292f'\n\n return (\n <Card padding={3} radius={2} tone=\"default\" border>\n <Stack space={3}>\n {/* Header */}\n <Flex align=\"center\" justify=\"space-between\" gap={3}>\n <Text weight=\"semibold\" size={1}>\n Meta Tags Preview\n </Text>\n <Flex gap={2} align=\"center\">\n <Text size={0} muted>\n {tagCount} tags generated\n </Text>\n <Button\n text={copyLabel}\n tone=\"default\"\n mode=\"ghost\"\n fontSize={1}\n padding={2}\n onClick={handleCopy}\n />\n </Flex>\n </Flex>\n\n {/* Toggle */}\n <button\n type=\"button\"\n onClick={() => setExpanded((p) => !p)}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 6,\n width: '100%',\n background: headerBg,\n border: `1px solid ${headerBorder}`,\n borderRadius: 6,\n padding: '6px 12px',\n cursor: 'pointer',\n fontSize: 12,\n fontWeight: 500,\n color: headerText,\n }}\n >\n <span style={{fontSize: 10}}>{expanded ? '▲' : '▼'}</span>\n {expanded ? 'Hide tags' : 'Show tags'}\n </button>\n\n {/* Highlighted code block */}\n {expanded && (\n <div\n style={{\n borderRadius: 6,\n border: `1px solid ${codeBorder}`,\n overflow: 'hidden',\n }}\n >\n {/* Code header bar */}\n <div\n style={{\n background: headerBg,\n borderBottom: `1px solid ${headerBorder}`,\n padding: '6px 14px',\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n }}\n >\n <span\n style={{\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: isDark ? '#388bfd' : '#0550ae',\n display: 'inline-block',\n }}\n />\n <span style={{fontSize: 11, color: headerText, fontFamily: 'monospace'}}>HTML</span>\n </div>\n\n {/* Tokens */}\n <Box\n as=\"pre\"\n padding={3}\n style={{\n margin: 0,\n maxHeight: 420,\n overflow: 'auto',\n fontSize: 12,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace',\n lineHeight: 1.7,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n backgroundColor: codeBg,\n }}\n >\n {tokens.map((token, idx) => (\n <span key={idx} style={{color: token.color}}>\n {token.text}\n </span>\n ))}\n </Box>\n </div>\n )}\n </Stack>\n </Card>\n )\n}\n\nexport default MetaTagsPreview\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useEffect, useMemo, useState} from 'react'\nimport {PatchEvent, set, StringInputProps, useClient, useFormValue} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {FeedbackType} from '../../types'\nimport {getMetaTitleValidationMessages} from '../../utils/seoUtils'\nimport SeoGenButton from '../ai/SeoGenButton'\n\nconst MetaTitle = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path, schemaType, onChange} = props\n const {options} = schemaType as {\n options?: {\n apiVersion?: string\n titleSuffix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffixQuery?: string\n ai?: AiConfig\n }\n }\n\n const parent = useFormValue([path[0]]) as {keywords?: string[]; _type?: string}\n const isParentseoField = parent && parent?._type === 'seoFields'\n const keywords = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const rootDoc =\n (useFormValue([]) as ({_type?: string; _id?: string} & Record<string, unknown>) | null) ?? {}\n const client = useClient({apiVersion: options?.apiVersion ?? '2024-01-01'})\n const [groqTitleSuffix, setGroqTitleSuffix] = useState<string>('')\n const [isDuplicateTitle, setIsDuplicateTitle] = useState(false)\n\n const titleSuffixQuery = options?.titleSuffixQuery\n const titleSuffixOption = options?.titleSuffix\n\n useEffect(() => {\n if (!titleSuffixQuery) return\n client\n .fetch<string>(titleSuffixQuery)\n .then((result) => {\n setGroqTitleSuffix(result === null || result === undefined ? '' : String(result))\n })\n .catch(() => {\n setGroqTitleSuffix('')\n })\n }, [titleSuffixQuery, client])\n\n const currentDocId = rootDoc._id ?? ''\n useEffect(() => {\n if (!value?.trim() || !currentDocId) {\n setIsDuplicateTitle(false)\n return () => {}\n }\n const timer = setTimeout(() => {\n client\n .fetch<number>(\n `count(*[seo.title == $title && _id != $id && !(_id in path(\"drafts.**\"))])`,\n {title: value, id: currentDocId},\n )\n .then((count) => setIsDuplicateTitle(count > 0))\n .catch(() => setIsDuplicateTitle(false))\n }, 600)\n return () => clearTimeout(timer)\n }, [value, client, currentDocId])\n\n const resolvedSuffix = useMemo((): string => {\n if (titleSuffixQuery) return groqTitleSuffix\n if (!titleSuffixOption) return ''\n if (typeof titleSuffixOption === 'function') {\n return titleSuffixOption(rootDoc)\n }\n return titleSuffixOption\n }, [titleSuffixQuery, groqTitleSuffix, titleSuffixOption, rootDoc])\n\n // ` | ` separator = 3 chars\n const suffixLength = resolvedSuffix ? resolvedSuffix.length + 3 : 0\n\n const feedbackItems = useMemo(\n () => getMetaTitleValidationMessages(value || '', keywords, isParentseoField, suffixLength),\n [value, keywords, isParentseoField, suffixLength],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <SeoGenButton\n field=\"title\"\n ai={options?.ai}\n seoFieldPath={String(path[0])}\n onGenerate={(v) => onChange(PatchEvent.from(set(v)))}\n />\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 {isDuplicateTitle && (\n <div style={{display: 'flex', alignItems: 'center', gap: 7}}>\n <div\n style={{\n minWidth: 10,\n height: 10,\n borderRadius: '50%',\n backgroundColor: 'orange',\n }}\n />\n <Text weight=\"bold\" muted size={14}>\n Duplicate meta title — another published document uses this exact title.\n </Text>\n </div>\n )}\n </Stack>\n </Stack>\n )\n}\n\nexport default MetaTitle\n","import {AiConfig, AllFieldKeys, SeoFieldsPluginConfig, ValidHiddenFieldKeys} from '../plugin'\n\nexport function withLicense(\n ai: AiConfig | undefined,\n licenseKey: string | undefined,\n): AiConfig | undefined {\n if (!ai) return undefined\n return licenseKey ? {...ai, _licenseKey: licenseKey} : ai\n}\n\nconst DEFAULT_FIELD_INFO: Record<AllFieldKeys, {title: string; description: string}> = {\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:\n 'Configure how search engine crawlers should index, follow links, translate content, and index images 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 openGraphTitle: {\n title: 'Open Graph Title',\n description: '',\n },\n openGraphUrl: {\n title: 'Open Graph URL',\n description:\n 'The canonical URL of the page. This should be the full URL including protocol (https://).',\n },\n openGraphDescription: {\n title: 'Open Graph Description',\n description: '',\n },\n openGraphSiteName: {\n title: 'Open Graph Site Name',\n description: 'The name of your website. This is often the site title.',\n },\n openGraphType: {\n title: 'Open Graph Type',\n description: 'Select the type of content for Open Graph.',\n },\n openGraphImageType: {\n title: 'Image Type',\n description: 'Choose whether to upload an image or provide an image URL for Open Graph.',\n },\n openGraphImage: {\n title: 'Open Graph Image',\n description:\n 'Recommended size: 1200x630px (minimum 600x315px). Max file size: 5MB. Aspect ratio 1.91:1.',\n },\n openGraphImageUrl: {\n title: 'Open Graph Image 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 twitterCard: {\n title: 'X Card Type',\n description: '',\n },\n twitterSite: {\n title: 'Site X Handle',\n description: 'The X (formerly Twitter) handle of the website (e.g., @example)',\n },\n twitterCreator: {\n title: 'X Creator Handle',\n description: 'The X (formerly Twitter) handle of the content creator (e.g., @creator)',\n },\n twitterTitle: {\n title: 'X Title',\n description: 'The title of the content as it should appear on X (formerly Twitter).',\n },\n twitterDescription: {\n title: 'X Description',\n description: 'A brief description of the content for X (formerly Twitter).',\n },\n twitterImageType: {\n title: 'X Image Type',\n description:\n 'Choose whether to upload an image or provide an image URL for X (formerly Twitter).',\n },\n twitterImage: {\n title: 'X Image',\n description:\n 'An image URL which should be at least 120x120px for \"summary\" card and 280x150px for \"summary_large_image\" card.',\n },\n twitterImageUrl: {\n title: 'X Image URL',\n description:\n 'Enter the full URL of the image for X (formerly Twitter). Ensure the image is accessible and meets the recommended size.',\n },\n focusKeyword: {\n title: 'Focus Keyword',\n description:\n 'The primary keyword you want this page to rank for. Used to check keyword placement in title, description, and URL.',\n },\n hreflangs: {\n title: 'Hreflang Entries',\n description:\n 'Add alternate language/region versions of this page. Include x-default for the fallback URL.',\n },\n geoChecklist: {\n title: 'AI Overview Readiness',\n description: 'Checklist of signals that improve visibility in AI-generated overviews.',\n },\n metaTagsPreview: {\n title: 'Meta Tags Preview',\n description: 'Preview the full HTML <head> meta tags generated for this page.',\n },\n}\n\nexport const getFieldInfo = (\n fieldName: string,\n fieldOverrides: SeoFieldsPluginConfig['fieldOverrides'],\n): {title: string; description: string} => {\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\n/**\n * Check if a field should be hidden based on the current document type\n */\nexport const isFieldHidden = (\n fieldName: ValidHiddenFieldKeys,\n config: SeoFieldsPluginConfig,\n documentType?: string,\n): boolean => {\n // Check if field is in default hidden fields\n if (config.defaultHiddenFields?.includes(fieldName)) {\n return true\n }\n\n // Check if field is hidden for specific post type\n if (documentType && config.fieldVisibility?.[documentType]?.hiddenFields?.includes(fieldName)) {\n return true\n }\n\n return false\n}\n\n/**\n * Get the hidden function for any field\n */\nexport const getFieldHiddenFunction = (\n fieldName: ValidHiddenFieldKeys,\n config: SeoFieldsPluginConfig,\n) => {\n return ({\n document,\n }: {\n document?: {\n _type?: string\n }\n }): boolean => {\n const documentType = document?._type\n return isFieldHidden(fieldName, config, documentType)\n }\n}\n","export const isEmpty = (value: unknown): boolean => {\n return (\n value === null ||\n value === undefined ||\n (typeof value === 'string' && value.trim() === '') ||\n (Array.isArray(value) && value.length === 0) ||\n (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0)\n )\n}\n","import {defineField, defineType, SchemaTypeDefinition} from 'sanity'\n\nimport OgDescription from '../../../components/openGraph/OgDescription'\nimport OgImage from '../../../components/openGraph/OgImage'\nimport OgImageUrl from '../../../components/openGraph/OgImageUrl'\nimport OgTitle from '../../../components/openGraph/OgTitle'\nimport {SeoFieldsPluginConfig} from '../../../plugin'\nimport {getFieldHiddenFunction, getFieldInfo, withLicense} from '../../../utils/fieldsUtils'\n\nexport default function openGraph(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {\n return defineType({\n name: 'openGraph',\n title: 'Open Graph Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'url',\n type: 'url',\n ...getFieldInfo('openGraphUrl', config.fieldOverrides),\n hidden: getFieldHiddenFunction('openGraphUrl', config),\n description:\n 'The canonical URL of the page. This should be the full URL including protocol (https://).',\n }),\n defineField({\n name: 'title',\n ...getFieldInfo('openGraphTitle', config.fieldOverrides),\n type: 'string',\n hidden: getFieldHiddenFunction('openGraphTitle', config),\n components: {\n input: OgTitle,\n },\n options: {\n ...(config.ai ? {ai: withLicense(config.ai, config.licenseKey)} : {}),\n } as Record<string, unknown>,\n }),\n defineField({\n name: 'description',\n ...getFieldInfo('openGraphDescription', config.fieldOverrides),\n type: 'text',\n rows: 3,\n hidden: getFieldHiddenFunction('openGraphDescription', config),\n components: {\n input: OgDescription,\n },\n options: {\n ...(config.ai ? {ai: withLicense(config.ai, config.licenseKey)} : {}),\n } as Record<string, unknown>,\n }),\n defineField({\n name: 'siteName',\n ...getFieldInfo('openGraphSiteName', config.fieldOverrides),\n type: 'string',\n hidden: getFieldHiddenFunction('openGraphSiteName', config),\n }),\n defineField({\n name: 'type',\n ...getFieldInfo('openGraphType', config.fieldOverrides),\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 hidden: getFieldHiddenFunction('openGraphType', config),\n initialValue: 'website',\n }),\n defineField({\n name: 'imageType',\n ...getFieldInfo('openGraphImageType', config.fieldOverrides),\n type: 'string',\n options: {\n list: [\n {title: 'Upload Image', value: 'upload'},\n {title: 'Image URL', value: 'url'},\n ],\n },\n hidden: getFieldHiddenFunction('openGraphImage', config),\n initialValue: 'upload',\n }),\n defineField({\n name: 'image',\n ...getFieldInfo('openGraphImage', config.fieldOverrides),\n type: 'image',\n options: {\n hotspot: true,\n },\n components: {\n input: OgImage,\n },\n fields: [\n defineField({\n name: 'alt',\n title: 'Image Alt Text',\n type: 'string',\n description: 'A description of the image for accessibility purposes.',\n }),\n ],\n hidden: (context) => {\n const {parent} = context\n if (parent?.imageType !== 'upload') return true\n const hiddenFn = getFieldHiddenFunction('openGraphImage', config)\n return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn\n },\n }),\n defineField({\n name: 'imageUrl',\n ...getFieldInfo('openGraphImageUrl', config.fieldOverrides),\n type: 'url',\n components: {\n input: OgImageUrl,\n },\n hidden: (context) => {\n const {parent} = context\n if (parent?.imageType !== 'url') return true\n const hiddenFn = getFieldHiddenFunction('openGraphImage', config)\n return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn\n },\n }),\n ],\n })\n}\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {PatchEvent, set, StringInputProps, useFormValue} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {FeedbackType} from '../../types'\nimport {getOgDescriptionValidation} from '../../utils/seoUtils'\nimport SeoGenButton from '../ai/SeoGenButton'\n\nconst OgDescription = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path, schemaType, onChange} = props\n const {options} = schemaType as {options?: {ai?: AiConfig}}\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 = useMemo(() => parent?.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 <SeoGenButton\n field=\"ogDescription\"\n ai={options?.ai}\n seoFieldPath={String(path[0])}\n onGenerate={(v) => onChange(PatchEvent.from(set(v)))}\n />\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {ObjectInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getOgImageValidation} from '../../utils/seoUtils'\n\nconst OgImage = (props: ObjectInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n const imgValue = value as Record<string, unknown> | undefined\n const hasImage = !!imgValue?.asset\n const altText = imgValue?.alt as string | undefined\n\n const feedbackItems = useMemo(\n () => getOgImageValidation(hasImage, altText, seoParent),\n [hasImage, altText, seoParent],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 OgImage\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getOgImageUrlValidation} from '../../utils/seoUtils'\n\nconst OgImageUrl = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n\n const feedbackItems = useMemo(() => getOgImageUrlValidation(value, seoParent), [value, seoParent])\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 OgImageUrl\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactNode, useMemo} from 'react'\nimport {PatchEvent, set, StringInputProps, useFormValue} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {FeedbackType} from '../../types'\nimport {getOgTitleValidation} from '../../utils/seoUtils'\nimport SeoGenButton from '../ai/SeoGenButton'\n\nconst OgTitle = (props: StringInputProps): ReactNode => {\n const {value, renderDefault, path, schemaType, onChange} = props\n const {options} = schemaType as {options?: {ai?: AiConfig}}\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 = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const feedbackItems = useMemo(\n () => getOgTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <SeoGenButton\n field=\"ogTitle\"\n ai={options?.ai}\n seoFieldPath={String(path[0])}\n onGenerate={(v) => onChange(PatchEvent.from(set(v)))}\n />\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 {defineField, defineType, SchemaTypeDefinition} from 'sanity'\n\nimport TwitterDescription from '../../../components/twitter/twitterDescription'\nimport TwitterImage from '../../../components/twitter/TwitterImage'\nimport TwitterImageUrl from '../../../components/twitter/TwitterImageUrl'\nimport TwitterTitle from '../../../components/twitter/twitterTitle'\nimport {SeoFieldsPluginConfig} from '../../../plugin'\nimport {getFieldHiddenFunction, getFieldInfo, withLicense} from '../../../utils/fieldsUtils'\n\nexport default function twitter(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {\n return defineType({\n name: 'twitter',\n title: 'X (Formerly Twitter)',\n type: 'object',\n fields: [\n defineField({\n name: 'card',\n ...getFieldInfo('twitterCard', config.fieldOverrides),\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 hidden: getFieldHiddenFunction('twitterCard', config),\n initialValue: 'summary_large_image', // good default\n }),\n defineField({\n name: 'site',\n ...getFieldInfo('twitterSite', config.fieldOverrides),\n type: 'string',\n hidden: getFieldHiddenFunction('twitterSite', config),\n }),\n defineField({\n name: 'creator',\n type: 'string',\n ...getFieldInfo('twitterCreator', config.fieldOverrides),\n hidden: getFieldHiddenFunction('twitterCreator', config),\n }),\n defineField({\n name: 'title',\n type: 'string',\n ...getFieldInfo('twitterTitle', config.fieldOverrides),\n hidden: getFieldHiddenFunction('twitterTitle', config),\n components: {\n input: TwitterTitle,\n },\n options: {\n ...(config.ai ? {ai: withLicense(config.ai, config.licenseKey)} : {}),\n } as Record<string, unknown>,\n }),\n defineField({\n name: 'description',\n type: 'text',\n rows: 3,\n ...getFieldInfo('twitterDescription', config.fieldOverrides),\n hidden: getFieldHiddenFunction('twitterDescription', config),\n components: {\n input: TwitterDescription,\n },\n options: {\n ...(config.ai ? {ai: withLicense(config.ai, config.licenseKey)} : {}),\n } as Record<string, unknown>,\n }),\n defineField({\n name: 'imageType',\n ...getFieldInfo('twitterImageType', config.fieldOverrides),\n type: 'string',\n options: {\n list: [\n {title: 'Upload Image', value: 'upload'},\n {title: 'Image URL', value: 'url'},\n ],\n },\n hidden: getFieldHiddenFunction('twitterImage', config),\n initialValue: 'upload',\n }),\n defineField({\n name: 'image',\n ...getFieldInfo('twitterImage', config.fieldOverrides),\n type: 'image',\n options: {\n hotspot: true,\n },\n components: {\n input: TwitterImage,\n },\n fields: [\n defineField({\n name: 'alt',\n title: 'Image Alt Text',\n type: 'string',\n description: 'A description of the image for accessibility purposes.',\n }),\n ],\n hidden: (context) => {\n const {parent} = context\n if (parent?.imageType !== 'upload') return true\n const hiddenFn = getFieldHiddenFunction('twitterImage', config)\n return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn\n },\n }),\n defineField({\n name: 'imageUrl',\n ...getFieldInfo('twitterImageUrl', config.fieldOverrides),\n type: 'url',\n components: {\n input: TwitterImageUrl,\n },\n hidden: (context) => {\n const {parent} = context\n if (parent?.imageType !== 'url') return true\n const hiddenFn = getFieldHiddenFunction('twitterImage', config)\n return typeof hiddenFn === 'function' ? hiddenFn(context) : hiddenFn\n },\n }),\n ],\n })\n}\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {PatchEvent, set, StringInputProps, useFormValue} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {FeedbackType} from '../../types'\nimport {getTwitterDescriptionValidation} from '../../utils/seoUtils'\nimport SeoGenButton from '../ai/SeoGenButton'\n\nconst TwitterDescription = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path, schemaType, onChange} = props\n const {options} = schemaType as {options?: {ai?: AiConfig}}\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 = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const feedbackItems = useMemo(\n () => getTwitterDescriptionValidation(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <SeoGenButton\n field=\"twitterDescription\"\n ai={options?.ai}\n seoFieldPath={String(path[0])}\n onGenerate={(v) => onChange(PatchEvent.from(set(v)))}\n />\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {ObjectInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getTwitterImageValidation} from '../../utils/seoUtils'\n\nconst TwitterImage = (props: ObjectInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n const imgValue = value as Record<string, unknown> | undefined\n const hasImage = !!imgValue?.asset\n const altText = imgValue?.alt as string | undefined\n\n const feedbackItems = useMemo(\n () => getTwitterImageValidation(hasImage, altText, seoParent),\n [hasImage, altText, seoParent],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 TwitterImage\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {StringInputProps, useFormValue} from 'sanity'\n\nimport {FeedbackType} from '../../types'\nimport {getTwitterImageUrlValidation} from '../../utils/seoUtils'\n\nconst TwitterImageUrl = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path} = props\n\n const seoParent = useFormValue([path[0]]) as Record<string, unknown> | null\n\n const feedbackItems = useMemo(\n () => getTwitterImageUrlValidation(value, seoParent),\n [value, seoParent],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 TwitterImageUrl\n","import {Stack, Text} from '@sanity/ui'\nimport {type ReactElement, useMemo} from 'react'\nimport {PatchEvent, set, StringInputProps, useFormValue} from 'sanity'\n\nimport type {AiConfig} from '../../plugin'\nimport {FeedbackType} from '../../types'\nimport {getTwitterTitleValidation} from '../../utils/seoUtils'\nimport SeoGenButton from '../ai/SeoGenButton'\n\nconst TwitterTitle = (props: StringInputProps): ReactElement => {\n const {value, renderDefault, path, schemaType, onChange} = props\n const {options} = schemaType as {options?: {ai?: AiConfig}}\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 = useMemo(() => parent?.keywords || [], [parent?.keywords])\n\n const feedbackItems = useMemo(\n () => getTwitterTitleValidation(value || '', keywords, isParentseoField),\n [value, keywords, isParentseoField],\n )\n\n return (\n <Stack space={3}>\n {renderDefault(props)}\n <SeoGenButton\n field=\"twitterTitle\"\n ai={options?.ai}\n seoFieldPath={String(path[0])}\n onGenerate={(v) => onChange(PatchEvent.from(set(v)))}\n />\n <Stack space={2}>\n {feedbackItems.map((item: FeedbackType) => (\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 {defineField, defineType, SchemaTypeDefinition} from 'sanity'\n\nimport MetaDescription from '../../../components/meta/MetaDescription'\nimport MetaImage from '../../../components/meta/MetaImage'\nimport MetaTitle from '../../../components/meta/MetaTitle'\nimport {SeoFieldsPluginConfig} from '../../../plugin'\nimport {getFieldHiddenFunction, getFieldInfo} from '../../../utils/fieldsUtils'\n\nexport default function baseMeta(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition {\n return defineType({\n name: 'baseMeta',\n title: 'Basic Meta Settings',\n type: 'object',\n fields: [\n defineField({\n name: 'title',\n ...getFieldInfo('title', config.fieldOverrides),\n type: 'string',\n components: {\n input: MetaTitle,\n },\n hidden: getFieldHiddenFunction('title', config),\n }),\n defineField({\n name: 'description',\n ...getFieldInfo('description', config.fieldOverrides),\n type: 'text',\n rows: 3,\n components: {\n input: MetaDescription,\n },\n hidden: getFieldHiddenFunction('description', config),\n }),\n defineField({\n name: 'metaImage',\n ...getFieldInfo('metaImage', config.fieldOverrides),\n type: 'image',\n options: {\n hotspot: true,\n },\n components: {\n input: MetaImage,\n },\n hidden: getFieldHiddenFunction('metaImage', config),\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 hidden: getFieldHiddenFunction('keywords', config),\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 hidden: getFieldHiddenFunction('canonicalUrl', config),\n }),\n defineField({\n name: 'metaAttributes',\n ...getFieldInfo('metaAttributes', config.fieldOverrides),\n type: 'array',\n of: [{type: 'metaAttribute'}],\n hidden: getFieldHiddenFunction('metaAttributes', config),\n }),\n ],\n })\n}\n","import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n name: 'hreflangEntry',\n title: 'Hreflang Entry',\n type: 'object',\n fields: [\n defineField({\n name: 'locale',\n title: 'Language / Region',\n type: 'string',\n description: 'BCP 47 tag — e.g. en, fr-FR, x-default',\n validation: (Rule) =>\n Rule.required().custom((value: string | undefined) => {\n if (!value) return 'Required'\n if (!/^(x-default|[a-z]{2,3}(-[A-Z]{2,3})?)$/.test(value)) {\n return 'Must be a valid BCP 47 tag (e.g. en, fr-FR, x-default)'\n }\n return true\n }),\n }),\n defineField({\n name: 'url',\n title: 'URL',\n type: 'url',\n validation: (Rule) => Rule.required().uri({scheme: ['http', 'https']}),\n }),\n ],\n preview: {\n select: {\n title: 'locale',\n subtitle: 'url',\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({attributeName, attributeValueString, attributeValueImage}) {\n let subtitle = ''\n if (attributeValueString) {\n subtitle = `Value: ${attributeValueString}`\n } else if (attributeValueImage) {\n subtitle = 'Value: [Image]'\n } else {\n subtitle = 'No Attribute Value'\n }\n return {\n title: attributeName || 'No Attribute Name',\n subtitle: subtitle,\n media: attributeValueImage,\n }\n },\n },\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:\n 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',\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 defineField({\n name: 'noTranslate',\n title: 'No Translate',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent search engines from offering a translated version of this page in search results.',\n }),\n defineField({\n name: 'noImageIndex',\n title: 'No Image Index',\n type: 'boolean',\n initialValue: false,\n description:\n 'Enable this to prevent images on this page from appearing in image search results.',\n }),\n ],\n description:\n 'Select how search engines should index, follow links, translate content, and index images on this page.',\n})\n","import {SchemaTypeDefinition} from 'sanity'\n\nimport {SeoFieldsPluginConfig} from '../../plugin'\nimport seoFields from '..'\nimport baseMeta from './baseMeta'\nimport hreflangEntry from './hreflang'\nimport metaAttribute from './metaAttribute'\nimport metaTag from './metaTag'\nimport openGraph from './openGraph'\nimport robots from './robots'\nimport twitter from './twitter'\n\nexport default function types(config: SeoFieldsPluginConfig = {}): SchemaTypeDefinition[] {\n return [\n seoFields(config), // pass config here\n baseMeta(config), // pass config here\n openGraph(config), // pass config here\n twitter(config), // pass config here\n metaAttribute,\n metaTag,\n robots,\n hreflangEntry,\n ]\n}\n","export type {SeoHealthPaneOptions} from 'seofields-pro'\nexport {createSeoHealthPane} from 'seofields-pro'\nexport {createSeoHealthPaneDefault as default} from 'seofields-pro'\n","import type {SeoHealthMetrics, SeoHealthStatus} from '../types'\nimport {getFocusKeywordPlacement, hasKeywordOveruse, hasMatchingKeyword} from './seoUtils'\n\nconst getStatusCategory = (score: number): SeoHealthStatus => {\n if (score >= 80) return 'excellent'\n if (score >= 60) return 'good'\n if (score >= 40) return 'fair'\n if (score > 0) return 'poor'\n return 'missing'\n}\n\nconst scoreMetaTitle = (title?: string): {score: number; issues: string[]} => {\n const issues: string[] = []\n let score = 0\n\n if (title && title.length >= 50 && title.length <= 60) {\n score = 15\n } else if (title && title.length > 0) {\n score = 10\n if (title.length < 50) issues.push('Meta title too short (< 50 chars)')\n if (title.length > 60) issues.push('Meta title too long (> 60 chars)')\n } else {\n issues.push('Missing meta title')\n }\n\n return {score, issues}\n}\n\nconst scoreMetaDescription = (description?: string): {score: number; issues: string[]} => {\n const issues: string[] = []\n let score = 0\n\n if (description && description.length >= 120 && description.length <= 160) {\n score = 15\n } else if (description && description.length > 0) {\n score = 10\n if (description.length < 120) issues.push('Meta description too short (< 120 chars)')\n if (description.length > 160) issues.push('Meta description too long (> 160 chars)')\n } else {\n issues.push('Missing meta description')\n }\n\n return {score, issues}\n}\n\nconst scoreOpenGraph = (openGraph?: Record<string, unknown>): {score: number; issues: string[]} => {\n const issues: string[] = []\n let score = 0\n\n if (openGraph) {\n if (openGraph.title) score += 6\n else issues.push('Missing OG title')\n\n if (openGraph.description) score += 6\n else issues.push('Missing OG description')\n\n if (openGraph.image) score += 6\n else issues.push('Missing OG image')\n\n if (openGraph.type) score += 7\n else issues.push('Missing OG type')\n } else {\n issues.push('Open Graph not configured')\n }\n\n return {score, issues}\n}\n\nconst scoreTwitterCard = (twitter?: Record<string, unknown>): {score: number; issues: string[]} => {\n const issues: string[] = []\n let score = 0\n\n if (twitter) {\n if (twitter.title) score += 5\n else issues.push('Missing Twitter title')\n\n if (twitter.description) score += 5\n else issues.push('Missing Twitter description')\n\n if (twitter.image) score += 5\n else issues.push('Missing Twitter image')\n } else {\n issues.push('Twitter Card not configured')\n }\n\n return {score, issues}\n}\n\ninterface KeywordScoreResult {\n score: number\n issues: string[]\n stuffed: boolean\n}\n\n// Rewards keywords that are actually used in title/description, not just declared.\nconst scoreKeywordArray = (\n keywords: string[] | undefined,\n title: string | undefined,\n description: string | undefined,\n): KeywordScoreResult => {\n if (!keywords || keywords.length === 0) {\n return {score: 0, issues: ['No keywords defined'], stuffed: false}\n }\n\n const issues: string[] = []\n let score = 2\n if (\n hasMatchingKeyword(title || '', keywords) ||\n hasMatchingKeyword(description || '', keywords)\n ) {\n score += 3\n } else {\n issues.push('Keywords defined but not used in title or description')\n }\n\n const stuffed =\n hasKeywordOveruse(title || '', keywords) || hasKeywordOveruse(description || '', keywords)\n if (stuffed) issues.push('Keyword stuffing detected — reduce repeated keyword usage')\n\n return {score, issues, stuffed}\n}\n\nconst scoreFocusKeyword = (\n focusKeyword: string | undefined,\n title: string | undefined,\n description: string | undefined,\n): KeywordScoreResult => {\n const placement = getFocusKeywordPlacement(focusKeyword, title, description)\n if (!placement.hasFocusKeyword) {\n return {score: 0, issues: ['No focus keyword defined'], stuffed: false}\n }\n\n const issues: string[] = []\n let score = 1\n if (placement.atStartOfTitle) {\n score += 4\n } else if (placement.inTitle) {\n score += 2\n issues.push('Focus keyword is in the title but not at the start')\n } else if (placement.inDescription) {\n score += 1\n issues.push('Focus keyword missing from meta title')\n } else {\n issues.push('Focus keyword missing from meta title and description')\n }\n\n if (placement.isStuffed)\n issues.push('Focus keyword appears too many times in title — avoid stuffing')\n\n return {score, issues, stuffed: placement.isStuffed}\n}\n\n// Combined budget stays within the 10 points historically allotted to \"keywords\" so the 100-point total is unchanged.\nconst scoreKeywordOptimization = (\n keywords: string[] | undefined,\n focusKeyword: string | undefined,\n title: string | undefined,\n description: string | undefined,\n): {score: number; issues: string[]} => {\n const arrayResult = scoreKeywordArray(keywords, title, description)\n const focusResult = scoreFocusKeyword(focusKeyword, title, description)\n\n let score = arrayResult.score + focusResult.score\n if (arrayResult.stuffed || focusResult.stuffed) score = Math.max(0, score - 2)\n\n return {score: Math.min(score, 10), issues: [...arrayResult.issues, ...focusResult.issues]}\n}\n\ninterface SeoDocument {\n seo?: Record<string, unknown>\n}\n\nexport const calculateHealthScore = (doc: SeoDocument): SeoHealthMetrics => {\n if (!doc.seo) {\n return {score: 0, status: 'missing', issues: ['SEO fields not configured']}\n }\n\n const seo = doc.seo\n const title = seo.title as string | undefined\n const description = seo.description as string | undefined\n const keywords = seo.keywords as string[] | undefined\n const focusKeyword = seo.focusKeyword as string | undefined\n const robots = seo.robots as\n | {noIndex?: boolean; noFollow?: boolean; noTranslate?: boolean; noImageIndex?: boolean}\n | undefined\n const openGraph = seo.openGraph as Record<string, unknown> | undefined\n const twitter = seo.twitter as Record<string, unknown> | undefined\n let score = 0\n const issues: string[] = []\n\n const titleResult = scoreMetaTitle(title)\n score += titleResult.score\n issues.push(...titleResult.issues)\n\n const descResult = scoreMetaDescription(description)\n score += descResult.score\n issues.push(...descResult.issues)\n\n if (seo.metaImage) score += 10\n else issues.push('Missing meta image')\n\n const keywordResult = scoreKeywordOptimization(keywords, focusKeyword, title, description)\n score += keywordResult.score\n issues.push(...keywordResult.issues)\n\n if (robots && !robots.noIndex) score += 5\n else if (!robots) score += 5\n\n const ogResult = scoreOpenGraph(openGraph)\n score += ogResult.score\n issues.push(...ogResult.issues)\n\n const twResult = scoreTwitterCard(twitter)\n score += twResult.score\n issues.push(...twResult.issues)\n\n const hasMetaImage = !!seo.metaImage\n const hasOgImage = !!(openGraph && openGraph.image)\n const hasTwitterImage = !!(twitter && twitter.image)\n if (hasMetaImage && hasOgImage && hasTwitterImage) {\n score += 5\n } else {\n const missingImages: string[] = []\n if (!hasMetaImage) missingImages.push('meta image')\n if (!hasOgImage) missingImages.push('OG image')\n if (!hasTwitterImage) missingImages.push('Twitter image')\n issues.push(`Missing images for full score: ${missingImages.join(', ')}`)\n }\n\n const status = getStatusCategory(score)\n return {score, status, issues}\n}\n","// Import the plugin\nimport seofields from './plugin'\n\n// Default export the plugin\nexport default seofields\n\n// Re-export everything from plugin.ts\nexport * from './plugin'\n\n// Export schema types for external use\nexport {default as seoFieldsSchema} from './schemas'\nexport {default as allSchemas} from './schemas/types'\nexport {default as baseMetaSchema} from './schemas/types/baseMeta'\nexport {default as metaAttributeSchema} from './schemas/types/metaAttribute'\nexport {default as metaTagSchema} from './schemas/types/metaTag'\nexport {default as openGraphSchema} from './schemas/types/openGraph'\nexport {default as robotsSchema} from './schemas/types/robots'\nexport {default as twitterSchema} from './schemas/types/twitter'\n\n// Export dashboard factory (lazy — avoids styled-components at import time)\nexport type {SeoHealthPaneOptions} from './components/SeoHealthPane'\nexport {createSeoHealthPane} from './components/SeoHealthPane'\n\n// Export Schema.org Studio preview components\nexport type {\n SchemaOrgJsonLdPreviewOptions,\n SchemaOrgJsonLdPreviewProps,\n} from './schema/components/SchemaOrgJsonLdPreview'\nexport {\n SchemaOrgJsonLdPreview as SchemaOrgDocumentJsonLdPreview,\n SchemaOrgJsonLdPreview,\n} from './schema/components/SchemaOrgJsonLdPreview'\n\n// Export types\nexport type {DocumentWithSeoHealth, SeoHealthMetrics, SeoHealthStatus} from './types'\n\n// Export shared scoring utility\nexport {calculateHealthScore} from './utils/scoring'\n"]}
|