sanity-plugin-seofields 1.6.1 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +15 -3
  2. package/dist/SeoHealthDashboard-BEZVLLFG.js +4 -0
  3. package/dist/{SeoHealthDashboard-PY5MKJCZ.js.map → SeoHealthDashboard-BEZVLLFG.js.map} +1 -1
  4. package/dist/SeoHealthDashboard-D3XU3CUB.cjs +10 -0
  5. package/dist/{SeoHealthDashboard-FZTP4GS7.cjs.map → SeoHealthDashboard-D3XU3CUB.cjs.map} +1 -1
  6. package/dist/{SeoHealthTool-Z3O6NEKF.cjs → SeoHealthTool-4VZMD4TR.cjs} +4 -4
  7. package/dist/{SeoHealthTool-Z3O6NEKF.cjs.map → SeoHealthTool-4VZMD4TR.cjs.map} +1 -1
  8. package/dist/{SeoHealthTool-SKY7FAKK.js → SeoHealthTool-BSTYAKQS.js} +3 -3
  9. package/dist/{SeoHealthTool-SKY7FAKK.js.map → SeoHealthTool-BSTYAKQS.js.map} +1 -1
  10. package/dist/{SeoPreview-LWSWTIF5.cjs → SeoPreview-PYYVZMY3.cjs} +3 -3
  11. package/dist/SeoPreview-PYYVZMY3.cjs.map +1 -0
  12. package/dist/{SeoPreview-BQBKMYSG.js → SeoPreview-XVAZYHCL.js} +3 -3
  13. package/dist/SeoPreview-XVAZYHCL.js.map +1 -0
  14. package/dist/{chunk-77U7CMJP.cjs → chunk-BHIZQLY7.cjs} +135 -16
  15. package/dist/chunk-BHIZQLY7.cjs.map +1 -0
  16. package/dist/{chunk-KFFDQX22.js → chunk-HQTYQMP5.js} +135 -16
  17. package/dist/chunk-HQTYQMP5.js.map +1 -0
  18. package/dist/cli.js +1 -1
  19. package/dist/index.cjs +3 -3
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +8 -6
  22. package/dist/index.d.ts +8 -6
  23. package/dist/index.js +3 -3
  24. package/dist/index.js.map +1 -1
  25. package/package.json +19 -4
  26. package/dist/SeoHealthDashboard-FZTP4GS7.cjs +0 -10
  27. package/dist/SeoHealthDashboard-PY5MKJCZ.js +0 -4
  28. package/dist/SeoPreview-BQBKMYSG.js.map +0 -1
  29. package/dist/SeoPreview-LWSWTIF5.cjs.map +0 -1
  30. package/dist/chunk-77U7CMJP.cjs.map +0 -1
  31. package/dist/chunk-KFFDQX22.js.map +0 -1
package/README.md CHANGED
@@ -1,16 +1,26 @@
1
- # 🔍 Sanity SEO Fields Plugin
1
+ # sanity-plugin-seofields
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/sanity-plugin-seofields.svg?color=brightgreen&label=npm)](https://www.npmjs.com/package/sanity-plugin-seofields)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/sanity-plugin-seofields.svg?color=blue)](https://www.npmjs.com/package/sanity-plugin-seofields)
5
5
  [![license](https://img.shields.io/npm/l/sanity-plugin-seofields.svg?color=yellow)](./LICENSE)
6
6
  [![GitHub stars](https://img.shields.io/github/stars/hardik-143/sanity-plugin-seofields?style=social)](https://github.com/hardik-143/sanity-plugin-seofields)
7
7
 
8
- A Sanity Studio (v3/v4/v5) plugin to manage SEO fields — meta tags, Open Graph, Twitter Cards, robots directives, and structured data.
8
+ **The only Sanity SEO plugin with a built-in audit dashboard.**
9
+
10
+ Manage meta tags, Open Graph, Twitter Cards, robots directives, and 38 Schema.org/JSON-LD types — all inside Sanity Studio v3/v4/v5.
9
11
 
10
12
  📖 **[Full Documentation →](https://sanity-plugin-seofields.thehardik.in/docs)**
11
13
 
12
14
  ---
13
15
 
16
+ > ⭐ If this plugin saves you time, please [leave a rating on the Sanity Plugin Directory](https://www.sanity.io/plugins/sanity-plugin-seofields), [star it on GitHub](https://github.com/hardik-143/sanity-plugin-seofields), and [leave a review on the docs site](https://sanity-plugin-seofields.thehardik.in/reviews). It helps other developers find it.
17
+
18
+ <!-- Demo GIF: SEO Health Dashboard in action -->
19
+ <!-- Add a screen recording here showing the SEO Health Dashboard scoring documents, the live SERP preview, and the Schema.org JSON-LD editor -->
20
+ <!-- Recommended tools: LICEcap (Mac/Windows), Kap (Mac), or ScreenToGif (Windows) -->
21
+
22
+ ---
23
+
14
24
  ## Installation
15
25
 
16
26
  ```bash
@@ -91,7 +101,9 @@ seofields({
91
101
 
92
102
  ## SEO Health Dashboard
93
103
 
94
- An optional Studio tool that scores SEO completeness across all documents, highlights missing fields, and links directly to documents.
104
+ > **This is the feature that sets this plugin apart from every other Sanity SEO plugin.**
105
+
106
+ A built-in Studio tool that audits SEO completeness across all your documents at once — scores each document, highlights missing fields, and links directly to what needs fixing. No external tools. No leaving Studio.
95
107
 
96
108
  Requires a free license key — [get yours here](https://sanity-plugin-seofields.thehardik.in/get-license).
97
109
 
@@ -0,0 +1,4 @@
1
+ export { SeoHealthDashboard_default as default } from './chunk-HQTYQMP5.js';
2
+ import './chunk-2NMEKWO5.js';
3
+ //# sourceMappingURL=SeoHealthDashboard-BEZVLLFG.js.map
4
+ //# sourceMappingURL=SeoHealthDashboard-BEZVLLFG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"SeoHealthDashboard-PY5MKJCZ.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"SeoHealthDashboard-BEZVLLFG.js"}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var chunkBHIZQLY7_cjs = require('./chunk-BHIZQLY7.cjs');
4
+ require('./chunk-S367Y35J.cjs');
5
+
6
+
7
+
8
+ module.exports = chunkBHIZQLY7_cjs.SeoHealthDashboard_default;
9
+ //# sourceMappingURL=SeoHealthDashboard-D3XU3CUB.cjs.map
10
+ //# sourceMappingURL=SeoHealthDashboard-D3XU3CUB.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"SeoHealthDashboard-FZTP4GS7.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"SeoHealthDashboard-D3XU3CUB.cjs"}
@@ -1,14 +1,14 @@
1
1
  'use strict';
2
2
 
3
- var chunk77U7CMJP_cjs = require('./chunk-77U7CMJP.cjs');
3
+ var chunkBHIZQLY7_cjs = require('./chunk-BHIZQLY7.cjs');
4
4
  var chunkS367Y35J_cjs = require('./chunk-S367Y35J.cjs');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
7
7
  var SeoHealthTool = (props) => {
8
- return /* @__PURE__ */ jsxRuntime.jsx(chunk77U7CMJP_cjs.SeoHealthDashboard_default, chunkS367Y35J_cjs.__spreadValues({}, props));
8
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkBHIZQLY7_cjs.SeoHealthDashboard_default, chunkS367Y35J_cjs.__spreadValues({}, props));
9
9
  };
10
10
  var SeoHealthTool_default = SeoHealthTool;
11
11
 
12
12
  module.exports = SeoHealthTool_default;
13
- //# sourceMappingURL=SeoHealthTool-Z3O6NEKF.cjs.map
14
- //# sourceMappingURL=SeoHealthTool-Z3O6NEKF.cjs.map
13
+ //# sourceMappingURL=SeoHealthTool-4VZMD4TR.cjs.map
14
+ //# sourceMappingURL=SeoHealthTool-4VZMD4TR.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/SeoHealthTool.tsx"],"names":["jsx","SeoHealthDashboard_default"],"mappings":";;;;;;AAMA,IAAM,aAAA,GAAgB,CAAC,KAAA,KAAmC;AACxD,EAAA,uBAAOA,cAAA,CAACC,mFAAuB,KAAA,CAAO,CAAA;AACxC,CAAA;AAEA,IAAO,qBAAA,GAAQ","file":"SeoHealthTool-Z3O6NEKF.cjs","sourcesContent":["import SeoHealthDashboard, {SeoHealthDashboardProps} from './SeoHealthDashboard'\n\n/**\n * Sanity Tool component for the SEO Health Dashboard\n * This component wraps the SeoHealthDashboard for use as a custom tool in Sanity Studio\n */\nconst SeoHealthTool = (props: SeoHealthDashboardProps) => {\n return <SeoHealthDashboard {...props} />\n}\n\nexport default SeoHealthTool\n"]}
1
+ {"version":3,"sources":["../src/components/SeoHealthTool.tsx"],"names":["jsx","SeoHealthDashboard_default"],"mappings":";;;;;;AAMA,IAAM,aAAA,GAAgB,CAAC,KAAA,KAAmC;AACxD,EAAA,uBAAOA,cAAA,CAACC,mFAAuB,KAAA,CAAO,CAAA;AACxC,CAAA;AAEA,IAAO,qBAAA,GAAQ","file":"SeoHealthTool-4VZMD4TR.cjs","sourcesContent":["import SeoHealthDashboard, {SeoHealthDashboardProps} from './SeoHealthDashboard'\n\n/**\n * Sanity Tool component for the SEO Health Dashboard\n * This component wraps the SeoHealthDashboard for use as a custom tool in Sanity Studio\n */\nconst SeoHealthTool = (props: SeoHealthDashboardProps) => {\n return <SeoHealthDashboard {...props} />\n}\n\nexport default SeoHealthTool\n"]}
@@ -1,4 +1,4 @@
1
- import { SeoHealthDashboard_default } from './chunk-KFFDQX22.js';
1
+ import { SeoHealthDashboard_default } from './chunk-HQTYQMP5.js';
2
2
  import { __spreadValues } from './chunk-2NMEKWO5.js';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
@@ -8,5 +8,5 @@ var SeoHealthTool = (props) => {
8
8
  var SeoHealthTool_default = SeoHealthTool;
9
9
 
10
10
  export { SeoHealthTool_default as default };
11
- //# sourceMappingURL=SeoHealthTool-SKY7FAKK.js.map
12
- //# sourceMappingURL=SeoHealthTool-SKY7FAKK.js.map
11
+ //# sourceMappingURL=SeoHealthTool-BSTYAKQS.js.map
12
+ //# sourceMappingURL=SeoHealthTool-BSTYAKQS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/SeoHealthTool.tsx"],"names":[],"mappings":";;;;AAMA,IAAM,aAAA,GAAgB,CAAC,KAAA,KAAmC;AACxD,EAAA,uBAAO,GAAA,CAAC,+CAAuB,KAAA,CAAO,CAAA;AACxC,CAAA;AAEA,IAAO,qBAAA,GAAQ","file":"SeoHealthTool-SKY7FAKK.js","sourcesContent":["import SeoHealthDashboard, {SeoHealthDashboardProps} from './SeoHealthDashboard'\n\n/**\n * Sanity Tool component for the SEO Health Dashboard\n * This component wraps the SeoHealthDashboard for use as a custom tool in Sanity Studio\n */\nconst SeoHealthTool = (props: SeoHealthDashboardProps) => {\n return <SeoHealthDashboard {...props} />\n}\n\nexport default SeoHealthTool\n"]}
1
+ {"version":3,"sources":["../src/components/SeoHealthTool.tsx"],"names":[],"mappings":";;;;AAMA,IAAM,aAAA,GAAgB,CAAC,KAAA,KAAmC;AACxD,EAAA,uBAAO,GAAA,CAAC,+CAAuB,KAAA,CAAO,CAAA;AACxC,CAAA;AAEA,IAAO,qBAAA,GAAQ","file":"SeoHealthTool-BSTYAKQS.js","sourcesContent":["import SeoHealthDashboard, {SeoHealthDashboardProps} from './SeoHealthDashboard'\n\n/**\n * Sanity Tool component for the SEO Health Dashboard\n * This component wraps the SeoHealthDashboard for use as a custom tool in Sanity Studio\n */\nconst SeoHealthTool = (props: SeoHealthDashboardProps) => {\n return <SeoHealthDashboard {...props} />\n}\n\nexport default SeoHealthTool\n"]}
@@ -166,7 +166,7 @@ var SeoPreview = (props) => {
166
166
  /* @__PURE__ */ jsxRuntime.jsxs(PreviewBody, { children: [
167
167
  /* @__PURE__ */ jsxRuntime.jsx(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
168
168
  /* @__PURE__ */ jsxRuntime.jsx(SerpTitle, { children: title && title.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
169
- chunkIFDLKZET_cjs.truncate(title, Math.max(1, 60 - titleSuffix.length)),
169
+ chunkIFDLKZET_cjs.truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 2 : 0))),
170
170
  titleSuffix && /* @__PURE__ */ jsxRuntime.jsxs(
171
171
  "span",
172
172
  {
@@ -185,5 +185,5 @@ var SeoPreview = (props) => {
185
185
  var SeoPreview_default = SeoPreview;
186
186
 
187
187
  module.exports = SeoPreview_default;
188
- //# sourceMappingURL=SeoPreview-LWSWTIF5.cjs.map
189
- //# sourceMappingURL=SeoPreview-LWSWTIF5.cjs.map
188
+ //# sourceMappingURL=SeoPreview-PYYVZMY3.cjs.map
189
+ //# sourceMappingURL=SeoPreview-PYYVZMY3.cjs.map
@@ -0,0 +1 @@
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-PYYVZMY3.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 ? titleSuffix.length + 2 : 0)))}\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"]}
@@ -160,7 +160,7 @@ var SeoPreview = (props) => {
160
160
  /* @__PURE__ */ jsxs(PreviewBody, { children: [
161
161
  /* @__PURE__ */ jsx(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
162
162
  /* @__PURE__ */ jsx(SerpTitle, { children: title && title.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
163
- truncate(title, Math.max(1, 60 - titleSuffix.length)),
163
+ truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 2 : 0))),
164
164
  titleSuffix && /* @__PURE__ */ jsxs(
165
165
  "span",
166
166
  {
@@ -179,5 +179,5 @@ var SeoPreview = (props) => {
179
179
  var SeoPreview_default = SeoPreview;
180
180
 
181
181
  export { SeoPreview_default as default };
182
- //# sourceMappingURL=SeoPreview-BQBKMYSG.js.map
183
- //# sourceMappingURL=SeoPreview-BQBKMYSG.js.map
182
+ //# sourceMappingURL=SeoPreview-XVAZYHCL.js.map
183
+ //# sourceMappingURL=SeoPreview-XVAZYHCL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/SeoPreview.tsx"],"names":[],"mappings":"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","file":"SeoPreview-XVAZYHCL.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 ? titleSuffix.length + 2 : 0)))}\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"]}
@@ -1046,6 +1046,81 @@ var RenderLicenseInvalid = ({ licenseKey, validateLicense }) => /* @__PURE__ */
1046
1046
  }
1047
1047
  )
1048
1048
  ] }) }) });
1049
+ var LegacyBannerWrap = styled__default.default.div`
1050
+ display: flex;
1051
+ align-items: flex-start;
1052
+ gap: 12px;
1053
+ padding: 14px 20px;
1054
+ margin-bottom: 20px;
1055
+ background: #fffbeb;
1056
+ border-bottom: 1px solid #fde68a;
1057
+ color: #92400e;
1058
+ font-size: 13px;
1059
+ line-height: 1.5;
1060
+ `;
1061
+ var LegacyBannerLink = styled__default.default.a`
1062
+ display: inline-block;
1063
+ padding: 5px 14px;
1064
+ background: #f59e0b;
1065
+ color: #fff;
1066
+ border-radius: 6px;
1067
+ font-size: 12px;
1068
+ font-weight: 600;
1069
+ text-decoration: none;
1070
+ &:hover {
1071
+ background: #d97706;
1072
+ }
1073
+ `;
1074
+ var ButtonWrapper = styled__default.default.div`
1075
+ display: flex;
1076
+ gap: 12px;
1077
+ flex-wrap: wrap;
1078
+ align-items: center;
1079
+ margin-top: 6px;
1080
+ `;
1081
+ var VerifyButton = styled__default.default.button`
1082
+ display: inline-block;
1083
+ padding: 5px 14px;
1084
+ background: #10b981;
1085
+ color: #fff;
1086
+ border-radius: 6px;
1087
+ font-size: 12px;
1088
+ font-weight: 600;
1089
+ border: none;
1090
+ cursor: pointer;
1091
+ text-decoration: none;
1092
+ &:hover {
1093
+ background: #059669;
1094
+ }
1095
+ `;
1096
+ var RenderLegacyBanner = ({ upgradeUrl, cutoffDate, onVerifyUpgrade }) => {
1097
+ const deadline = cutoffDate ? new Date(cutoffDate).toLocaleDateString("en-US", {
1098
+ month: "long",
1099
+ day: "numeric",
1100
+ year: "numeric"
1101
+ }) : null;
1102
+ return /* @__PURE__ */ jsxRuntime.jsxs(LegacyBannerWrap, { children: [
1103
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "18px", lineHeight: 1 }, children: "\u26A0\uFE0F" }),
1104
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1105
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Legacy free access \u2014 upgrade required" }),
1106
+ /* @__PURE__ */ jsxRuntime.jsx("br", {}),
1107
+ deadline ? `Your free beta access expires on ${deadline}. After that date, a paid license is required.` : "Your free beta access will be transitioning to paid. Upgrade now to keep access.",
1108
+ /* @__PURE__ */ jsxRuntime.jsx("br", {}),
1109
+ /* @__PURE__ */ jsxRuntime.jsxs(ButtonWrapper, { children: [
1110
+ /* @__PURE__ */ jsxRuntime.jsx(
1111
+ LegacyBannerLink,
1112
+ {
1113
+ href: upgradeUrl != null ? upgradeUrl : "https://sanity-plugin-seofields.thehardik.in/get-license",
1114
+ target: "_blank",
1115
+ rel: "noopener noreferrer",
1116
+ children: "Upgrade for $10/project \u2192"
1117
+ }
1118
+ ),
1119
+ onVerifyUpgrade && /* @__PURE__ */ jsxRuntime.jsx(VerifyButton, { onClick: onVerifyUpgrade, children: "Already paid? Click here to verify your license \u2192" })
1120
+ ] })
1121
+ ] })
1122
+ ] });
1123
+ };
1049
1124
  var scoreMetaTitle = (title) => {
1050
1125
  const issues = [];
1051
1126
  let score = 0;
@@ -1330,7 +1405,10 @@ var SeoHealthDashboard = ({
1330
1405
  return Array.from(groups.values());
1331
1406
  }, [allDeprecationWarnings]);
1332
1407
  const client = sanity.useClient({ apiVersion });
1333
- const [licenseStatus, setLicenseStatus] = react.useState("loading");
1408
+ const [licenseStatus, setLicenseStatus] = react.useState(
1409
+ "loading"
1410
+ );
1411
+ const [legacyInfo, setLegacyInfo] = react.useState({});
1334
1412
  const [documents, setDocuments] = react.useState([]);
1335
1413
  const [loading, setLoading] = react.useState(true);
1336
1414
  const [isRefreshing, setIsRefreshing] = react.useState(false);
@@ -1419,6 +1497,28 @@ var SeoHealthDashboard = ({
1419
1497
  }
1420
1498
  setCurrentPage(1);
1421
1499
  }, []);
1500
+ const applyLicenseState = react.useCallback(
1501
+ ({ valid, legacy = false, upgradeUrl, cutoffDate }) => {
1502
+ if (valid && legacy) {
1503
+ setLegacyInfo({ upgradeUrl, cutoffDate });
1504
+ setLicenseStatus("legacy");
1505
+ return;
1506
+ }
1507
+ setLegacyInfo({});
1508
+ setLicenseStatus(valid ? "valid" : "invalid");
1509
+ },
1510
+ []
1511
+ );
1512
+ const readCachedLicenseState = react.useCallback((cacheKey) => {
1513
+ try {
1514
+ const cached = sessionStorage.getItem(cacheKey);
1515
+ if (!cached) return null;
1516
+ const parsed = JSON.parse(cached);
1517
+ return Date.now() - parsed.ts < CACHE_TTL_MS ? parsed : null;
1518
+ } catch (e) {
1519
+ return null;
1520
+ }
1521
+ }, []);
1422
1522
  const validateLicense = react.useCallback(
1423
1523
  async (forceRefresh = false) => {
1424
1524
  var _a;
@@ -1439,16 +1539,10 @@ var SeoHealthDashboard = ({
1439
1539
  }
1440
1540
  }
1441
1541
  if (!forceRefresh) {
1442
- try {
1443
- const cached = sessionStorage.getItem(cacheKey);
1444
- if (cached) {
1445
- const { valid, ts } = JSON.parse(cached);
1446
- if (Date.now() - ts < CACHE_TTL_MS) {
1447
- setLicenseStatus(valid ? "valid" : "invalid");
1448
- return;
1449
- }
1450
- }
1451
- } catch (e) {
1542
+ const cachedLicenseState = readCachedLicenseState(cacheKey);
1543
+ if (cachedLicenseState) {
1544
+ applyLicenseState(cachedLicenseState);
1545
+ return;
1452
1546
  }
1453
1547
  }
1454
1548
  setLicenseStatus("loading");
@@ -1458,10 +1552,21 @@ var SeoHealthDashboard = ({
1458
1552
  headers: { "Content-Type": "application/json" },
1459
1553
  body: JSON.stringify({ licenseKey, projectId })
1460
1554
  });
1555
+ const data = await res.json().catch(() => ({}));
1461
1556
  const valid = res.ok;
1462
- setLicenseStatus(valid ? "valid" : "invalid");
1557
+ const legacy = valid && data.legacy === true;
1558
+ applyLicenseState({ valid, legacy, upgradeUrl: data.upgradeUrl, cutoffDate: data.cutoffDate });
1463
1559
  try {
1464
- sessionStorage.setItem(cacheKey, JSON.stringify({ valid, ts: Date.now() }));
1560
+ sessionStorage.setItem(
1561
+ cacheKey,
1562
+ JSON.stringify({
1563
+ valid,
1564
+ legacy,
1565
+ upgradeUrl: data.upgradeUrl,
1566
+ cutoffDate: data.cutoffDate,
1567
+ ts: Date.now()
1568
+ })
1569
+ );
1465
1570
  } catch (e) {
1466
1571
  }
1467
1572
  } catch (e) {
@@ -1469,8 +1574,11 @@ var SeoHealthDashboard = ({
1469
1574
  }
1470
1575
  },
1471
1576
  // eslint-disable-next-line react-hooks/exhaustive-deps
1472
- [licenseKey, previewMode]
1577
+ [applyLicenseState, licenseKey, previewMode, readCachedLicenseState]
1473
1578
  );
1579
+ const handleVerifyUpgrade = react.useCallback(() => {
1580
+ validateLicense(true);
1581
+ }, [validateLicense]);
1474
1582
  react.useEffect(() => {
1475
1583
  validateLicense();
1476
1584
  }, [licenseKey, previewMode]);
@@ -2041,11 +2149,22 @@ var SeoHealthDashboard = ({
2041
2149
  return /* @__PURE__ */ jsxRuntime.jsxs(DashboardContainer, { style: currentVars, children: [
2042
2150
  licenseStatus === "loading" && /* @__PURE__ */ jsxRuntime.jsx(RenderLicenseLoading, { text: loadingLicense }),
2043
2151
  licenseStatus === "invalid" && /* @__PURE__ */ jsxRuntime.jsx(RenderLicenseInvalid, { licenseKey, validateLicense }),
2152
+ licenseStatus === "legacy" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2153
+ /* @__PURE__ */ jsxRuntime.jsx(
2154
+ RenderLegacyBanner,
2155
+ {
2156
+ upgradeUrl: legacyInfo.upgradeUrl,
2157
+ cutoffDate: legacyInfo.cutoffDate,
2158
+ onVerifyUpgrade: handleVerifyUpgrade
2159
+ }
2160
+ ),
2161
+ renderDashboardContent()
2162
+ ] }),
2044
2163
  licenseStatus === "valid" && renderDashboardContent()
2045
2164
  ] });
2046
2165
  };
2047
2166
  var SeoHealthDashboard_default = SeoHealthDashboard;
2048
2167
 
2049
2168
  exports.SeoHealthDashboard_default = SeoHealthDashboard_default;
2050
- //# sourceMappingURL=chunk-77U7CMJP.cjs.map
2051
- //# sourceMappingURL=chunk-77U7CMJP.cjs.map
2169
+ //# sourceMappingURL=chunk-BHIZQLY7.cjs.map
2170
+ //# sourceMappingURL=chunk-BHIZQLY7.cjs.map