sanity-plugin-seofields 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +1 -1
  2. package/dist/SeoHealthDashboard-3VBITMWT.cjs +10 -0
  3. package/dist/SeoHealthDashboard-3VBITMWT.cjs.map +1 -0
  4. package/dist/SeoHealthDashboard-BNJBRQVN.js +4 -0
  5. package/dist/SeoHealthDashboard-BNJBRQVN.js.map +1 -0
  6. package/dist/SeoHealthTool-4P6JST5S.cjs +14 -0
  7. package/dist/SeoHealthTool-4P6JST5S.cjs.map +1 -0
  8. package/dist/SeoHealthTool-MQBE5YGN.js +12 -0
  9. package/dist/SeoHealthTool-MQBE5YGN.js.map +1 -0
  10. package/dist/SeoPreview-G3LPA7GV.js +148 -0
  11. package/dist/SeoPreview-G3LPA7GV.js.map +1 -0
  12. package/dist/SeoPreview-Y3CFDVBR.cjs +154 -0
  13. package/dist/SeoPreview-Y3CFDVBR.cjs.map +1 -0
  14. package/dist/chunk-25JLWVEU.js +472 -0
  15. package/dist/chunk-25JLWVEU.js.map +1 -0
  16. package/dist/chunk-2NMEKWO5.js +35 -0
  17. package/dist/chunk-2NMEKWO5.js.map +1 -0
  18. package/dist/chunk-527WXITP.js +428 -0
  19. package/dist/chunk-527WXITP.js.map +1 -0
  20. package/dist/chunk-6CYMVS3O.js +1245 -0
  21. package/dist/chunk-6CYMVS3O.js.map +1 -0
  22. package/dist/chunk-D2GWRRK5.cjs +1293 -0
  23. package/dist/chunk-D2GWRRK5.cjs.map +1 -0
  24. package/dist/chunk-DYVCCQHT.cjs +1400 -0
  25. package/dist/chunk-DYVCCQHT.cjs.map +1 -0
  26. package/dist/chunk-IFDLKZET.cjs +485 -0
  27. package/dist/chunk-IFDLKZET.cjs.map +1 -0
  28. package/dist/chunk-L3L3FSPJ.cjs +478 -0
  29. package/dist/chunk-L3L3FSPJ.cjs.map +1 -0
  30. package/dist/chunk-S367Y35J.cjs +39 -0
  31. package/dist/chunk-S367Y35J.cjs.map +1 -0
  32. package/dist/chunk-WUIZN7VI.js +1394 -0
  33. package/dist/chunk-WUIZN7VI.js.map +1 -0
  34. package/dist/cli.js +67 -0
  35. package/dist/define-cli.cjs +12 -0
  36. package/dist/define-cli.cjs.map +1 -0
  37. package/dist/define-cli.d.cts +42 -0
  38. package/dist/define-cli.d.ts +42 -0
  39. package/dist/define-cli.js +10 -0
  40. package/dist/define-cli.js.map +1 -0
  41. package/dist/index.cjs +154 -2365
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.js +109 -2303
  44. package/dist/index.js.map +1 -1
  45. package/dist/next.cjs +235 -1591
  46. package/dist/next.cjs.map +1 -1
  47. package/dist/next.js +7 -1482
  48. package/dist/next.js.map +1 -1
  49. package/dist/schema/next.cjs +200 -1542
  50. package/dist/schema/next.cjs.map +1 -1
  51. package/dist/schema/next.js +4 -1475
  52. package/dist/schema/next.js.map +1 -1
  53. package/dist/schema.cjs +252 -1514
  54. package/dist/schema.cjs.map +1 -1
  55. package/dist/schema.js +58 -1391
  56. package/dist/schema.js.map +1 -1
  57. package/package.json +21 -5
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![GitHub issues](https://img.shields.io/github/issues/hardik-143/sanity-plugin-seofields?color=orange)](https://github.com/hardik-143/sanity-plugin-seofields/issues)
7
7
  [![GitHub stars](https://img.shields.io/github/stars/hardik-143/sanity-plugin-seofields?style=social)](https://github.com/hardik-143/sanity-plugin-seofields)
8
8
 
9
- A comprehensive Sanity Studio v3 plugin to manage SEO fields like meta titles, descriptions, Open Graph tags, and X (Formerly Twitter) Cards for structured, search-optimized content.
9
+ A comprehensive Sanity Studio (v3/v4/v5) plugin to manage SEO fields like meta titles, descriptions, Open Graph tags, and X (Formerly Twitter) Cards for structured, search-optimized content.
10
10
 
11
11
  ## ✨ Features
12
12
 
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var chunkDYVCCQHT_cjs = require('./chunk-DYVCCQHT.cjs');
4
+ require('./chunk-S367Y35J.cjs');
5
+
6
+
7
+
8
+ module.exports = chunkDYVCCQHT_cjs.SeoHealthDashboard_default;
9
+ //# sourceMappingURL=SeoHealthDashboard-3VBITMWT.cjs.map
10
+ //# sourceMappingURL=SeoHealthDashboard-3VBITMWT.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"SeoHealthDashboard-3VBITMWT.cjs"}
@@ -0,0 +1,4 @@
1
+ export { SeoHealthDashboard_default as default } from './chunk-WUIZN7VI.js';
2
+ import './chunk-2NMEKWO5.js';
3
+ //# sourceMappingURL=SeoHealthDashboard-BNJBRQVN.js.map
4
+ //# sourceMappingURL=SeoHealthDashboard-BNJBRQVN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"SeoHealthDashboard-BNJBRQVN.js"}
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ var chunkDYVCCQHT_cjs = require('./chunk-DYVCCQHT.cjs');
4
+ var chunkS367Y35J_cjs = require('./chunk-S367Y35J.cjs');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ var SeoHealthTool = (props) => {
8
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkDYVCCQHT_cjs.SeoHealthDashboard_default, chunkS367Y35J_cjs.__spreadValues({}, props));
9
+ };
10
+ var SeoHealthTool_default = SeoHealthTool;
11
+
12
+ module.exports = SeoHealthTool_default;
13
+ //# sourceMappingURL=SeoHealthTool-4P6JST5S.cjs.map
14
+ //# sourceMappingURL=SeoHealthTool-4P6JST5S.cjs.map
@@ -0,0 +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-4P6JST5S.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"]}
@@ -0,0 +1,12 @@
1
+ import { SeoHealthDashboard_default } from './chunk-WUIZN7VI.js';
2
+ import { __spreadValues } from './chunk-2NMEKWO5.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var SeoHealthTool = (props) => {
6
+ return /* @__PURE__ */ jsx(SeoHealthDashboard_default, __spreadValues({}, props));
7
+ };
8
+ var SeoHealthTool_default = SeoHealthTool;
9
+
10
+ export { SeoHealthTool_default as default };
11
+ //# sourceMappingURL=SeoHealthTool-MQBE5YGN.js.map
12
+ //# sourceMappingURL=SeoHealthTool-MQBE5YGN.js.map
@@ -0,0 +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-MQBE5YGN.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"]}
@@ -0,0 +1,148 @@
1
+ import { truncate } from './chunk-25JLWVEU.js';
2
+ import './chunk-2NMEKWO5.js';
3
+ import { Box } from '@sanity/ui';
4
+ import { useFormValue } from 'sanity';
5
+ import styled from 'styled-components';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
+
8
+ var PreviewContainer = styled.div`
9
+ max-width: 600px;
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
+ background: #ffffff;
12
+ border: 1px solid #dadce0;
13
+ border-radius: 8px;
14
+ overflow: hidden;
15
+ `;
16
+ var PreviewHeader = styled.div`
17
+ background: #f8f9fa;
18
+ padding: 12px 16px;
19
+ border-bottom: 1px solid #dadce0;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: space-between;
23
+ gap: 8px;
24
+ `;
25
+ var PreviewBody = styled.div`
26
+ padding: 16px;
27
+ `;
28
+ var SerpUrl = styled.p`
29
+ margin: 0 0 4px;
30
+ color: #006621;
31
+ font-size: 13px;
32
+ line-height: 1.4;
33
+ word-break: break-word;
34
+ `;
35
+ var SerpTitle = styled.h3`
36
+ margin: 0 0 8px;
37
+ color: #1a0dab;
38
+ font-size: 18px;
39
+ font-weight: 500;
40
+ line-height: 1.4;
41
+ word-break: break-word;
42
+
43
+ &:hover {
44
+ text-decoration: underline;
45
+ }
46
+ `;
47
+ var SerpDescription = styled.p`
48
+ margin: 0;
49
+ color: #545454;
50
+ font-size: 14px;
51
+ line-height: 1.6;
52
+ word-break: break-word;
53
+ display: -webkit-box;
54
+ -webkit-line-clamp: 2;
55
+ -webkit-box-orient: vertical;
56
+ overflow: hidden;
57
+ `;
58
+ var LiveIndicator = styled.span`
59
+ display: inline-flex;
60
+ align-items: center;
61
+ gap: 4px;
62
+ font-size: 11px;
63
+ font-weight: 600;
64
+ text-transform: uppercase;
65
+ letter-spacing: 0.05em;
66
+ color: #4f46e5;
67
+ background: #f0f4ff;
68
+ padding: 4px 8px;
69
+ border-radius: 4px;
70
+ `;
71
+ var SeoPreview = (props) => {
72
+ var _a, _b;
73
+ const { path, schemaType } = props;
74
+ const { options } = schemaType;
75
+ const baseUrl = (options == null ? void 0 : options.baseUrl) || "https://www.example.com";
76
+ const prefixFunction = options == null ? void 0 : options.prefix;
77
+ const parent = useFormValue([path[0]]) || {
78
+ title: "",
79
+ description: "",
80
+ canonicalUrl: ""
81
+ };
82
+ const rootDoc = useFormValue([]) || {
83
+ slug: { current: "" }
84
+ };
85
+ const slug = ((_a = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a.current) || "";
86
+ const {
87
+ title,
88
+ description,
89
+ canonicalUrl: url
90
+ } = parent;
91
+ const base = (_b = url || baseUrl) == null ? void 0 : _b.replace(/\/+$/, "");
92
+ const slugStr = String(slug || "").replace(/^\/+/, "");
93
+ const pref = String(
94
+ prefixFunction ? prefixFunction(rootDoc) : ""
95
+ ).replace(/^\/+|\/+$/g, "");
96
+ const urlPath = [pref, slugStr].filter(Boolean).join("/");
97
+ const finalUrl = urlPath ? `${base}/${urlPath}` : base;
98
+ const domain = (() => {
99
+ try {
100
+ const u = new URL(finalUrl || base);
101
+ return u.hostname;
102
+ } catch (e) {
103
+ return "example.com";
104
+ }
105
+ })();
106
+ const urlDisplay = `${domain}${urlPath ? ` \u203A ${urlPath.split("/").slice(-1)[0]}` : ""}`;
107
+ return /* @__PURE__ */ jsx(Box, { padding: 3, children: /* @__PURE__ */ jsxs(PreviewContainer, { children: [
108
+ /* @__PURE__ */ jsxs(PreviewHeader, { children: [
109
+ /* @__PURE__ */ jsx(
110
+ "span",
111
+ {
112
+ style: {
113
+ fontSize: "11px",
114
+ color: "#5f6368",
115
+ textTransform: "uppercase",
116
+ letterSpacing: "0.05em"
117
+ },
118
+ children: "Search Preview"
119
+ }
120
+ ),
121
+ /* @__PURE__ */ jsxs(LiveIndicator, { children: [
122
+ /* @__PURE__ */ jsx(
123
+ "span",
124
+ {
125
+ style: {
126
+ width: "4px",
127
+ height: "4px",
128
+ borderRadius: "50%",
129
+ backgroundColor: "#4f46e5",
130
+ display: "inline-block"
131
+ }
132
+ }
133
+ ),
134
+ "Live"
135
+ ] })
136
+ ] }),
137
+ /* @__PURE__ */ jsxs(PreviewBody, { children: [
138
+ /* @__PURE__ */ jsx(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
139
+ /* @__PURE__ */ jsx(SerpTitle, { children: title && title.length > 0 ? truncate(title, 60) : "Your SEO Title will appear here" }),
140
+ /* @__PURE__ */ jsx(SerpDescription, { children: description && description.length > 0 ? truncate(description, 160) : "Your meta description will show up here. Make it compelling!" })
141
+ ] })
142
+ ] }) });
143
+ };
144
+ var SeoPreview_default = SeoPreview;
145
+
146
+ export { SeoPreview_default as default };
147
+ //# sourceMappingURL=SeoPreview-G3LPA7GV.js.map
148
+ //# sourceMappingURL=SeoPreview-G3LPA7GV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/SeoPreview.tsx"],"names":[],"mappings":"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","file":"SeoPreview-G3LPA7GV.js","sourcesContent":["import {Box, Stack, Text} from '@sanity/ui'\nimport React from 'react'\nimport {StringInputProps, 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): React.ReactElement => {\n const {path, schemaType} = props\n const {options} = schemaType as {\n options?: {\n baseUrl?: string\n prefix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | 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 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 // 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 ? truncate(title, 60) : 'Your SEO Title will appear here'}\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"]}
@@ -0,0 +1,154 @@
1
+ 'use strict';
2
+
3
+ var chunkIFDLKZET_cjs = require('./chunk-IFDLKZET.cjs');
4
+ require('./chunk-S367Y35J.cjs');
5
+ var ui = require('@sanity/ui');
6
+ var sanity = require('sanity');
7
+ var styled = require('styled-components');
8
+ var jsxRuntime = require('react/jsx-runtime');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var styled__default = /*#__PURE__*/_interopDefault(styled);
13
+
14
+ var PreviewContainer = styled__default.default.div`
15
+ max-width: 600px;
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ background: #ffffff;
18
+ border: 1px solid #dadce0;
19
+ border-radius: 8px;
20
+ overflow: hidden;
21
+ `;
22
+ var PreviewHeader = styled__default.default.div`
23
+ background: #f8f9fa;
24
+ padding: 12px 16px;
25
+ border-bottom: 1px solid #dadce0;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: space-between;
29
+ gap: 8px;
30
+ `;
31
+ var PreviewBody = styled__default.default.div`
32
+ padding: 16px;
33
+ `;
34
+ var SerpUrl = styled__default.default.p`
35
+ margin: 0 0 4px;
36
+ color: #006621;
37
+ font-size: 13px;
38
+ line-height: 1.4;
39
+ word-break: break-word;
40
+ `;
41
+ var SerpTitle = styled__default.default.h3`
42
+ margin: 0 0 8px;
43
+ color: #1a0dab;
44
+ font-size: 18px;
45
+ font-weight: 500;
46
+ line-height: 1.4;
47
+ word-break: break-word;
48
+
49
+ &:hover {
50
+ text-decoration: underline;
51
+ }
52
+ `;
53
+ var SerpDescription = styled__default.default.p`
54
+ margin: 0;
55
+ color: #545454;
56
+ font-size: 14px;
57
+ line-height: 1.6;
58
+ word-break: break-word;
59
+ display: -webkit-box;
60
+ -webkit-line-clamp: 2;
61
+ -webkit-box-orient: vertical;
62
+ overflow: hidden;
63
+ `;
64
+ var LiveIndicator = styled__default.default.span`
65
+ display: inline-flex;
66
+ align-items: center;
67
+ gap: 4px;
68
+ font-size: 11px;
69
+ font-weight: 600;
70
+ text-transform: uppercase;
71
+ letter-spacing: 0.05em;
72
+ color: #4f46e5;
73
+ background: #f0f4ff;
74
+ padding: 4px 8px;
75
+ border-radius: 4px;
76
+ `;
77
+ var SeoPreview = (props) => {
78
+ var _a, _b;
79
+ const { path, schemaType } = props;
80
+ const { options } = schemaType;
81
+ const baseUrl = (options == null ? void 0 : options.baseUrl) || "https://www.example.com";
82
+ const prefixFunction = options == null ? void 0 : options.prefix;
83
+ const parent = sanity.useFormValue([path[0]]) || {
84
+ title: "",
85
+ description: "",
86
+ canonicalUrl: ""
87
+ };
88
+ const rootDoc = sanity.useFormValue([]) || {
89
+ slug: { current: "" }
90
+ };
91
+ const slug = ((_a = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a.current) || "";
92
+ const {
93
+ title,
94
+ description,
95
+ canonicalUrl: url
96
+ } = parent;
97
+ const base = (_b = url || baseUrl) == null ? void 0 : _b.replace(/\/+$/, "");
98
+ const slugStr = String(slug || "").replace(/^\/+/, "");
99
+ const pref = String(
100
+ prefixFunction ? prefixFunction(rootDoc) : ""
101
+ ).replace(/^\/+|\/+$/g, "");
102
+ const urlPath = [pref, slugStr].filter(Boolean).join("/");
103
+ const finalUrl = urlPath ? `${base}/${urlPath}` : base;
104
+ const domain = (() => {
105
+ try {
106
+ const u = new URL(finalUrl || base);
107
+ return u.hostname;
108
+ } catch (e) {
109
+ return "example.com";
110
+ }
111
+ })();
112
+ const urlDisplay = `${domain}${urlPath ? ` \u203A ${urlPath.split("/").slice(-1)[0]}` : ""}`;
113
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(PreviewContainer, { children: [
114
+ /* @__PURE__ */ jsxRuntime.jsxs(PreviewHeader, { children: [
115
+ /* @__PURE__ */ jsxRuntime.jsx(
116
+ "span",
117
+ {
118
+ style: {
119
+ fontSize: "11px",
120
+ color: "#5f6368",
121
+ textTransform: "uppercase",
122
+ letterSpacing: "0.05em"
123
+ },
124
+ children: "Search Preview"
125
+ }
126
+ ),
127
+ /* @__PURE__ */ jsxRuntime.jsxs(LiveIndicator, { children: [
128
+ /* @__PURE__ */ jsxRuntime.jsx(
129
+ "span",
130
+ {
131
+ style: {
132
+ width: "4px",
133
+ height: "4px",
134
+ borderRadius: "50%",
135
+ backgroundColor: "#4f46e5",
136
+ display: "inline-block"
137
+ }
138
+ }
139
+ ),
140
+ "Live"
141
+ ] })
142
+ ] }),
143
+ /* @__PURE__ */ jsxRuntime.jsxs(PreviewBody, { children: [
144
+ /* @__PURE__ */ jsxRuntime.jsx(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
145
+ /* @__PURE__ */ jsxRuntime.jsx(SerpTitle, { children: title && title.length > 0 ? chunkIFDLKZET_cjs.truncate(title, 60) : "Your SEO Title will appear here" }),
146
+ /* @__PURE__ */ jsxRuntime.jsx(SerpDescription, { children: description && description.length > 0 ? chunkIFDLKZET_cjs.truncate(description, 160) : "Your meta description will show up here. Make it compelling!" })
147
+ ] })
148
+ ] }) });
149
+ };
150
+ var SeoPreview_default = SeoPreview;
151
+
152
+ module.exports = SeoPreview_default;
153
+ //# sourceMappingURL=SeoPreview-Y3CFDVBR.cjs.map
154
+ //# sourceMappingURL=SeoPreview-Y3CFDVBR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/SeoPreview.tsx"],"names":["styled","useFormValue","jsx","Box","jsxs","truncate"],"mappings":"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","file":"SeoPreview-Y3CFDVBR.cjs","sourcesContent":["import {Box, Stack, Text} from '@sanity/ui'\nimport React from 'react'\nimport {StringInputProps, 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): React.ReactElement => {\n const {path, schemaType} = props\n const {options} = schemaType as {\n options?: {\n baseUrl?: string\n prefix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | 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 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 // 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 ? truncate(title, 60) : 'Your SEO Title will appear here'}\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"]}