sanity-plugin-seofields 1.6.1 → 1.6.2
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/dist/{SeoPreview-LWSWTIF5.cjs → SeoPreview-PYYVZMY3.cjs} +3 -3
- package/dist/SeoPreview-PYYVZMY3.cjs.map +1 -0
- package/dist/{SeoPreview-BQBKMYSG.js → SeoPreview-XVAZYHCL.js} +3 -3
- package/dist/SeoPreview-XVAZYHCL.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/SeoPreview-BQBKMYSG.js.map +0 -1
- package/dist/SeoPreview-LWSWTIF5.cjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/SeoPreview.tsx"],"names":[],"mappings":";;;;;;;;AAOA,IAAM,mBAAmB,MAAA,CAAO,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAShC,IAAM,gBAAgB,MAAA,CAAO,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAU7B,IAAM,cAAc,MAAA,CAAO,GAAA;AAAA;AAAA,CAAA;AAI3B,IAAM,UAAU,MAAA,CAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAQvB,IAAM,YAAY,MAAA,CAAO,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA;AAazB,IAAM,kBAAkB,MAAA,CAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAY/B,IAAM,gBAAgB,MAAA,CAAO,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAc7B,IAAM,UAAA,GAAa,CAAC,KAAA,KAA0C;AA7E9D,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA8EE,EAAA,MAAM,EAAC,IAAA,EAAM,UAAA,EAAU,GAAI,KAAA;AAC3B,EAAA,MAAM,EAAC,SAAO,GAAI,UAAA;AAUlB,EAAA,MAAM,OAAA,GAAA,CAAU,mCAAS,OAAA,KAAW,yBAAA;AACpC,EAAA,MAAM,iBAAiB,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,MAAA;AAGhC,EAAA,MAAM,oBAAoB,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,WAAA;AACnC,EAAA,MAAM,uBAAA,GAAA,CAA0B,EAAA,GAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,uBAAA,KAAT,IAAA,GAAA,EAAA,GAAoC,KAAA;AACpE,EAAA,MAAM,mBAAmB,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,gBAAA;AAElC,EAAA,MAAM,MAAA,GAAS,UAAU,EAAC,UAAA,EAAA,CAAY,wCAAS,UAAA,KAAT,IAAA,GAAA,EAAA,GAAuB,cAAa,CAAA;AAC1E,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAiB,EAAE,CAAA;AAEjE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,gBAAA,EAAkB;AACvB,IAAA,MAAA,CACG,KAAA,CAAc,gBAAgB,CAAA,CAC9B,IAAA,CAAK,CAAC,MAAA,KAAW;AAChB,MAAA,kBAAA,CAAmB,WAAW,IAAA,IAAQ,MAAA,KAAW,SAAY,EAAA,GAAK,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,IAClF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,kBAAA,CAAmB,EAAE,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACL,CAAA,EAAG,CAAC,gBAAA,EAAkB,MAAM,CAAC,CAAA;AAC7B,EAAA,MAAM,SAAS,YAAA,CAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA,IAAK;AAAA,IACxC,KAAA,EAAO,EAAA;AAAA,IACP,WAAA,EAAa,EAAA;AAAA,IACb,YAAA,EAAc;AAAA,GAChB;AACA,EAAA,MAAM,OAAA,GAEF,YAAA,CAAa,EAAE,CAAA,IAAK;AAAA,IACtB,IAAA,EAAM,EAAC,OAAA,EAAS,EAAA;AAAE,GACpB;AACA,EAAA,MAAM,IAAA,GAAA,CAAA,CAAe,EAAA,GAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,IAAA,KAAT,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,OAAA,KAAW,EAAA;AAE/C,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,WAAA;AAAA,IACA,YAAA,EAAc;AAAA,GAChB,GAAI,MAAA;AAMJ,EAAA,MAAM,iBAAiB,MAAc;AACnC,IAAA,IAAI,kBAAkB,OAAO,eAAA;AAC7B,IAAA,IAAI,CAAC,mBAAmB,OAAO,EAAA;AAC/B,IAAA,IAAI,OAAO,sBAAsB,UAAA,EAAY;AAC3C,MAAA,OAAO,kBAAkB,OAAqD,CAAA;AAAA,IAChF;AACA,IAAA,OAAO,iBAAA;AAAA,EACT,CAAA;AACA,EAAA,MAAM,cAAsB,cAAA,EAAe;AAG3C,EAAA,MAAM,IAAA,GAAA,CAAQ,EAAA,GAAA,GAAA,IAAO,OAAA,KAAP,IAAA,GAAA,MAAA,GAAA,EAAA,CAAiB,QAAQ,MAAA,EAAQ,EAAA,CAAA;AAC/C,EAAA,MAAM,UAAU,MAAA,CAAO,IAAA,IAAQ,EAAE,CAAA,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACrD,EAAA,MAAM,IAAA,GAAO,MAAA;AAAA,IACX,cAAA,GAAiB,cAAA,CAAe,OAAqC,CAAA,GAAI;AAAA,GAC3E,CAAE,OAAA,CAAQ,YAAA,EAAc,EAAE,CAAA;AAC1B,EAAA,MAAM,OAAA,GAAU,CAAC,IAAA,EAAM,OAAO,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACxD,EAAA,MAAM,WAAW,OAAA,GAAU,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,GAAK,IAAA;AAGlD,EAAA,MAAM,UAAU,MAAM;AACpB,IAAA,IAAI;AACF,MAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,QAAA,IAAY,IAAI,CAAA;AAClC,MAAA,OAAO,CAAA,CAAE,QAAA;AAAA,IACX,CAAA,CAAA,OAAQ,CAAA,EAAA;AACN,MAAA,OAAO,aAAA;AAAA,IACT;AAAA,EACF,CAAA,GAAG;AAGH,EAAA,MAAM,aAAa,CAAA,EAAG,MAAM,CAAA,EAAG,OAAA,GAAU,WAAM,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA,CAAE,MAAM,EAAE,CAAA,CAAE,CAAC,CAAC,KAAK,EAAE,CAAA,CAAA;AAErF,EAAA,uBACE,GAAA,CAAC,GAAA,EAAA,EAAI,OAAA,EAAS,CAAA,EACZ,+BAAC,gBAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,aAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO;AAAA,YACL,QAAA,EAAU,MAAA;AAAA,YACV,KAAA,EAAO,SAAA;AAAA,YACP,aAAA,EAAe,WAAA;AAAA,YACf,aAAA,EAAe;AAAA,WACjB;AAAA,UACD,QAAA,EAAA;AAAA;AAAA,OAED;AAAA,2BACC,aAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO;AAAA,cACL,KAAA,EAAO,KAAA;AAAA,cACP,MAAA,EAAQ,KAAA;AAAA,cACR,YAAA,EAAc,KAAA;AAAA,cACd,eAAA,EAAiB,SAAA;AAAA,cACjB,OAAA,EAAS;AAAA;AACX;AAAA,SACF;AAAA,QAAE;AAAA,OAAA,EAEJ;AAAA,KAAA,EACF,CAAA;AAAA,yBAEC,WAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,OAAA,EAAA,EAAS,QAAA,EAAA,QAAA,GAAW,UAAA,GAAa,6BAAA,EAAyB,CAAA;AAAA,0BAC1D,SAAA,EAAA,EACE,QAAA,EAAA,KAAA,IAAS,KAAA,CAAM,MAAA,GAAS,oBACvB,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,QAAA,QAAA,CAAS,OAAO,IAAA,CAAK,GAAA,CAAI,GAAG,EAAA,GAAK,WAAA,CAAY,MAAM,CAAC,CAAA;AAAA,QACpD,WAAA,oBACC,IAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,OACE,uBAAA,GAA0B,MAAA,GAAY,EAAC,KAAA,EAAO,SAAA,EAAW,YAAY,GAAA,EAAG;AAAA,YAE3E,QAAA,EAAA;AAAA,cAAA,IAAA;AAAA,cACI;AAAA;AAAA;AAAA;AACL,OAAA,EAEJ,IAEA,iCAAA,EAEJ,CAAA;AAAA,sBACA,GAAA,CAAC,eAAA,EAAA,EACE,QAAA,EAAA,WAAA,IAAe,WAAA,CAAY,MAAA,GAAS,IACjC,QAAA,CAAS,WAAA,EAAa,GAAG,CAAA,GACzB,8DAAA,EACN;AAAA,KAAA,EACF;AAAA,GAAA,EACF,CAAA,EACF,CAAA;AAEJ,CAAA;AAEA,IAAO,kBAAA,GAAQ","file":"SeoPreview-BQBKMYSG.js","sourcesContent":["import {Box} from '@sanity/ui'\nimport {type ReactElement, useEffect, useState} from 'react'\nimport {StringInputProps, useClient, useFormValue} from 'sanity'\nimport styled from 'styled-components'\n\nimport {truncate} from '../utils/seoUtils'\n\nconst PreviewContainer = styled.div`\n max-width: 600px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: #ffffff;\n border: 1px solid #dadce0;\n border-radius: 8px;\n overflow: hidden;\n`\n\nconst PreviewHeader = styled.div`\n background: #f8f9fa;\n padding: 12px 16px;\n border-bottom: 1px solid #dadce0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n`\n\nconst PreviewBody = styled.div`\n padding: 16px;\n`\n\nconst SerpUrl = styled.p`\n margin: 0 0 4px;\n color: #006621;\n font-size: 13px;\n line-height: 1.4;\n word-break: break-word;\n`\n\nconst SerpTitle = styled.h3`\n margin: 0 0 8px;\n color: #1a0dab;\n font-size: 18px;\n font-weight: 500;\n line-height: 1.4;\n word-break: break-word;\n\n &:hover {\n text-decoration: underline;\n }\n`\n\nconst SerpDescription = styled.p`\n margin: 0;\n color: #545454;\n font-size: 14px;\n line-height: 1.6;\n word-break: break-word;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n`\n\nconst LiveIndicator = styled.span`\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: #4f46e5;\n background: #f0f4ff;\n padding: 4px 8px;\n border-radius: 4px;\n`\n\nconst SeoPreview = (props: StringInputProps): ReactElement => {\n const {path, schemaType} = props\n const {options} = schemaType as {\n options?: {\n baseUrl?: string\n apiVersion?: string\n prefix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffixInheritColor?: boolean\n titleSuffixQuery?: string\n }\n }\n const baseUrl = options?.baseUrl || 'https://www.example.com'\n const prefixFunction = options?.prefix as\n | ((doc: {_type?: string} & Record<string, unknown>) => string)\n | undefined\n const titleSuffixOption = options?.titleSuffix\n const titleSuffixInheritColor = options?.titleSuffixInheritColor ?? false\n const titleSuffixQuery = options?.titleSuffixQuery\n\n const client = useClient({apiVersion: options?.apiVersion ?? '2024-01-01'})\n const [groqTitleSuffix, setGroqTitleSuffix] = useState<string>('')\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 const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n const rootDoc: {\n slug?: {current: string}\n } = useFormValue([]) || {\n slug: {current: ''},\n }\n const slug: string = rootDoc?.slug?.current || ''\n\n const {\n title,\n description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n const getTitleSuffix = (): string => {\n if (titleSuffixQuery) return groqTitleSuffix\n if (!titleSuffixOption) return ''\n if (typeof titleSuffixOption === 'function') {\n return titleSuffixOption(rootDoc as {_type?: string} & Record<string, unknown>)\n }\n return titleSuffixOption\n }\n const titleSuffix: string = getTitleSuffix()\n\n // Build full URL\n const base = (url || baseUrl)?.replace(/\\/+$/, '')\n const slugStr = String(slug || '').replace(/^\\/+/, '')\n const pref = String(\n prefixFunction ? prefixFunction(rootDoc as {slug?: {current: string}}) : '',\n ).replace(/^\\/+|\\/+$/g, '')\n const urlPath = [pref, slugStr].filter(Boolean).join('/')\n const finalUrl = urlPath ? `${base}/${urlPath}` : base\n\n // Extract domain for display\n const domain = (() => {\n try {\n const u = new URL(finalUrl || base)\n return u.hostname\n } catch {\n return 'example.com'\n }\n })()\n\n // Format URL display with › separator\n const urlDisplay = `${domain}${urlPath ? ` › ${urlPath.split('/').slice(-1)[0]}` : ''}`\n\n return (\n <Box padding={3}>\n <PreviewContainer>\n <PreviewHeader>\n <span\n style={{\n fontSize: '11px',\n color: '#5f6368',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}\n >\n Search Preview\n </span>\n <LiveIndicator>\n <span\n style={{\n width: '4px',\n height: '4px',\n borderRadius: '50%',\n backgroundColor: '#4f46e5',\n display: 'inline-block',\n }}\n />\n Live\n </LiveIndicator>\n </PreviewHeader>\n\n <PreviewBody>\n <SerpUrl>{finalUrl ? urlDisplay : 'example.com › page-url'}</SerpUrl>\n <SerpTitle>\n {title && title.length > 0 ? (\n <>\n {truncate(title, Math.max(1, 60 - titleSuffix.length))}\n {titleSuffix && (\n <span\n style={\n titleSuffixInheritColor ? undefined : {color: '#70757a', fontWeight: 400}\n }\n >\n | {titleSuffix}\n </span>\n )}\n </>\n ) : (\n 'Your SEO Title will appear here'\n )}\n </SerpTitle>\n <SerpDescription>\n {description && description.length > 0\n ? truncate(description, 160)\n : 'Your meta description will show up here. Make it compelling!'}\n </SerpDescription>\n </PreviewBody>\n </PreviewContainer>\n </Box>\n )\n}\n\nexport default SeoPreview\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/SeoPreview.tsx"],"names":["styled","useClient","useState","useEffect","useFormValue","jsx","Box","jsxs","Fragment","truncate"],"mappings":"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","file":"SeoPreview-LWSWTIF5.cjs","sourcesContent":["import {Box} from '@sanity/ui'\nimport {type ReactElement, useEffect, useState} from 'react'\nimport {StringInputProps, useClient, useFormValue} from 'sanity'\nimport styled from 'styled-components'\n\nimport {truncate} from '../utils/seoUtils'\n\nconst PreviewContainer = styled.div`\n max-width: 600px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: #ffffff;\n border: 1px solid #dadce0;\n border-radius: 8px;\n overflow: hidden;\n`\n\nconst PreviewHeader = styled.div`\n background: #f8f9fa;\n padding: 12px 16px;\n border-bottom: 1px solid #dadce0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n`\n\nconst PreviewBody = styled.div`\n padding: 16px;\n`\n\nconst SerpUrl = styled.p`\n margin: 0 0 4px;\n color: #006621;\n font-size: 13px;\n line-height: 1.4;\n word-break: break-word;\n`\n\nconst SerpTitle = styled.h3`\n margin: 0 0 8px;\n color: #1a0dab;\n font-size: 18px;\n font-weight: 500;\n line-height: 1.4;\n word-break: break-word;\n\n &:hover {\n text-decoration: underline;\n }\n`\n\nconst SerpDescription = styled.p`\n margin: 0;\n color: #545454;\n font-size: 14px;\n line-height: 1.6;\n word-break: break-word;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n`\n\nconst LiveIndicator = styled.span`\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: #4f46e5;\n background: #f0f4ff;\n padding: 4px 8px;\n border-radius: 4px;\n`\n\nconst SeoPreview = (props: StringInputProps): ReactElement => {\n const {path, schemaType} = props\n const {options} = schemaType as {\n options?: {\n baseUrl?: string\n apiVersion?: string\n prefix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffixInheritColor?: boolean\n titleSuffixQuery?: string\n }\n }\n const baseUrl = options?.baseUrl || 'https://www.example.com'\n const prefixFunction = options?.prefix as\n | ((doc: {_type?: string} & Record<string, unknown>) => string)\n | undefined\n const titleSuffixOption = options?.titleSuffix\n const titleSuffixInheritColor = options?.titleSuffixInheritColor ?? false\n const titleSuffixQuery = options?.titleSuffixQuery\n\n const client = useClient({apiVersion: options?.apiVersion ?? '2024-01-01'})\n const [groqTitleSuffix, setGroqTitleSuffix] = useState<string>('')\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 const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n const rootDoc: {\n slug?: {current: string}\n } = useFormValue([]) || {\n slug: {current: ''},\n }\n const slug: string = rootDoc?.slug?.current || ''\n\n const {\n title,\n description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n const getTitleSuffix = (): string => {\n if (titleSuffixQuery) return groqTitleSuffix\n if (!titleSuffixOption) return ''\n if (typeof titleSuffixOption === 'function') {\n return titleSuffixOption(rootDoc as {_type?: string} & Record<string, unknown>)\n }\n return titleSuffixOption\n }\n const titleSuffix: string = getTitleSuffix()\n\n // Build full URL\n const base = (url || baseUrl)?.replace(/\\/+$/, '')\n const slugStr = String(slug || '').replace(/^\\/+/, '')\n const pref = String(\n prefixFunction ? prefixFunction(rootDoc as {slug?: {current: string}}) : '',\n ).replace(/^\\/+|\\/+$/g, '')\n const urlPath = [pref, slugStr].filter(Boolean).join('/')\n const finalUrl = urlPath ? `${base}/${urlPath}` : base\n\n // Extract domain for display\n const domain = (() => {\n try {\n const u = new URL(finalUrl || base)\n return u.hostname\n } catch {\n return 'example.com'\n }\n })()\n\n // Format URL display with › separator\n const urlDisplay = `${domain}${urlPath ? ` › ${urlPath.split('/').slice(-1)[0]}` : ''}`\n\n return (\n <Box padding={3}>\n <PreviewContainer>\n <PreviewHeader>\n <span\n style={{\n fontSize: '11px',\n color: '#5f6368',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}\n >\n Search Preview\n </span>\n <LiveIndicator>\n <span\n style={{\n width: '4px',\n height: '4px',\n borderRadius: '50%',\n backgroundColor: '#4f46e5',\n display: 'inline-block',\n }}\n />\n Live\n </LiveIndicator>\n </PreviewHeader>\n\n <PreviewBody>\n <SerpUrl>{finalUrl ? urlDisplay : 'example.com › page-url'}</SerpUrl>\n <SerpTitle>\n {title && title.length > 0 ? (\n <>\n {truncate(title, Math.max(1, 60 - titleSuffix.length))}\n {titleSuffix && (\n <span\n style={\n titleSuffixInheritColor ? undefined : {color: '#70757a', fontWeight: 400}\n }\n >\n | {titleSuffix}\n </span>\n )}\n </>\n ) : (\n 'Your SEO Title will appear here'\n )}\n </SerpTitle>\n <SerpDescription>\n {description && description.length > 0\n ? truncate(description, 160)\n : 'Your meta description will show up here. Make it compelling!'}\n </SerpDescription>\n </PreviewBody>\n </PreviewContainer>\n </Box>\n )\n}\n\nexport default SeoPreview\n"]}
|