sanity-plugin-seofields 1.6.5 → 1.8.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 (82) hide show
  1. package/README.md +355 -79
  2. package/dist/SchemaOrgJsonLdPreview-DJLkIPq4.d.cts +78 -0
  3. package/dist/SchemaOrgJsonLdPreview-DJLkIPq4.d.ts +78 -0
  4. package/dist/SeoHealthDashboard-AEKVVMUR-7NWV3C3Y.js +4 -0
  5. package/dist/SeoHealthDashboard-AEKVVMUR-7NWV3C3Y.js.map +1 -0
  6. package/dist/SeoHealthDashboard-AEKVVMUR-XWKJUH24.cjs +10 -0
  7. package/dist/SeoHealthDashboard-AEKVVMUR-XWKJUH24.cjs.map +1 -0
  8. package/dist/SeoHealthTool-2KKQJLJL.cjs +11 -0
  9. package/dist/{SeoHealthDashboard-BEZVLLFG.js.map → SeoHealthTool-2KKQJLJL.cjs.map} +1 -1
  10. package/dist/SeoHealthTool-3CUKGADZ.js +5 -0
  11. package/dist/{SeoHealthDashboard-D3XU3CUB.cjs.map → SeoHealthTool-3CUKGADZ.js.map} +1 -1
  12. package/dist/{SeoPreview-WYH7NYNM.cjs → SeoPreview-3EXR6FD4.cjs} +3 -3
  13. package/dist/{SeoPreview-WYH7NYNM.cjs.map → SeoPreview-3EXR6FD4.cjs.map} +1 -1
  14. package/dist/{SeoPreview-KEGGQSPJ.js → SeoPreview-4PPAF4NO.js} +3 -3
  15. package/dist/{SeoPreview-KEGGQSPJ.js.map → SeoPreview-4PPAF4NO.js.map} +1 -1
  16. package/dist/{chunk-S367Y35J.cjs → chunk-254YHUN3.cjs} +2 -15
  17. package/dist/{chunk-S367Y35J.cjs.map → chunk-254YHUN3.cjs.map} +1 -1
  18. package/dist/{chunk-HQTYQMP5.js → chunk-27XSYENH.js} +80 -29
  19. package/dist/chunk-27XSYENH.js.map +1 -0
  20. package/dist/{chunk-YHXUWGNA.cjs → chunk-B5IVI5LP.cjs} +918 -157
  21. package/dist/chunk-B5IVI5LP.cjs.map +1 -0
  22. package/dist/chunk-BNXMFD3T.cjs +431 -0
  23. package/dist/chunk-BNXMFD3T.cjs.map +1 -0
  24. package/dist/{chunk-2NMEKWO5.js → chunk-DDAAVRWG.js} +3 -15
  25. package/dist/{chunk-2NMEKWO5.js.map → chunk-DDAAVRWG.js.map} +1 -1
  26. package/dist/chunk-P6IOWIO2.cjs +480 -0
  27. package/dist/chunk-P6IOWIO2.cjs.map +1 -0
  28. package/dist/{chunk-XXA6WCWS.js → chunk-UOCZFYYP.js} +18 -203
  29. package/dist/chunk-UOCZFYYP.js.map +1 -0
  30. package/dist/{chunk-KTL6NYNG.js → chunk-VR44E62I.js} +897 -148
  31. package/dist/chunk-VR44E62I.js.map +1 -0
  32. package/dist/chunk-WKXHX3GO.js +424 -0
  33. package/dist/chunk-WKXHX3GO.js.map +1 -0
  34. package/dist/{chunk-BHIZQLY7.cjs → chunk-XZDLJ2N6.cjs} +84 -30
  35. package/dist/chunk-XZDLJ2N6.cjs.map +1 -0
  36. package/dist/cli.js +18 -18
  37. package/dist/{component-H52blyfc.d.ts → component-BzI-LHPw.d.cts} +9 -27
  38. package/dist/{component-CIE6Sy4F.d.cts → component-DEXwtemT.d.ts} +9 -27
  39. package/dist/define-cli.cjs +1 -1
  40. package/dist/define-cli.js +1 -1
  41. package/dist/index.cjs +1050 -94
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.d.cts +75 -202
  44. package/dist/index.d.ts +75 -202
  45. package/dist/index.js +1009 -63
  46. package/dist/index.js.map +1 -1
  47. package/dist/next.cjs +138 -116
  48. package/dist/next.cjs.map +1 -1
  49. package/dist/next.d.cts +9 -3
  50. package/dist/next.d.ts +9 -3
  51. package/dist/next.js +38 -32
  52. package/dist/next.js.map +1 -1
  53. package/dist/schema/next.cjs +105 -89
  54. package/dist/schema/next.d.cts +4 -4
  55. package/dist/schema/next.d.ts +4 -4
  56. package/dist/schema/next.js +4 -4
  57. package/dist/schema.cjs +219 -157
  58. package/dist/schema.cjs.map +1 -1
  59. package/dist/schema.d.cts +30 -16
  60. package/dist/schema.d.ts +30 -16
  61. package/dist/schema.js +41 -8
  62. package/dist/schema.js.map +1 -1
  63. package/dist/{types-Dp9Pfnt9.d.ts → types-BXSCbl6p.d.cts} +47 -2
  64. package/dist/{types-CyRdIF-3.d.cts → types-B_vjMPtM.d.cts} +1 -1
  65. package/dist/{types-BxcJinOf.d.cts → types-C19cQx5D.d.ts} +47 -2
  66. package/dist/{types-CbYb4MsN.d.ts → types-CW8qFdAn.d.ts} +1 -1
  67. package/dist/{types-BtSRRG6C.d.cts → types-yVmQfby9.d.cts} +8 -16
  68. package/dist/{types-BtSRRG6C.d.ts → types-yVmQfby9.d.ts} +8 -16
  69. package/package.json +2 -1
  70. package/dist/SeoHealthDashboard-BEZVLLFG.js +0 -4
  71. package/dist/SeoHealthDashboard-D3XU3CUB.cjs +0 -10
  72. package/dist/SeoHealthTool-4VZMD4TR.cjs +0 -14
  73. package/dist/SeoHealthTool-4VZMD4TR.cjs.map +0 -1
  74. package/dist/SeoHealthTool-BSTYAKQS.js +0 -12
  75. package/dist/SeoHealthTool-BSTYAKQS.js.map +0 -1
  76. package/dist/chunk-BCTPOWXA.cjs +0 -669
  77. package/dist/chunk-BCTPOWXA.cjs.map +0 -1
  78. package/dist/chunk-BHIZQLY7.cjs.map +0 -1
  79. package/dist/chunk-HQTYQMP5.js.map +0 -1
  80. package/dist/chunk-KTL6NYNG.js.map +0 -1
  81. package/dist/chunk-XXA6WCWS.js.map +0 -1
  82. package/dist/chunk-YHXUWGNA.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -2,13 +2,469 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ var chunkB5IVI5LP_cjs = require('./chunk-B5IVI5LP.cjs');
5
6
  var chunkZBHLMQTS_cjs = require('./chunk-ZBHLMQTS.cjs');
6
- var chunkS367Y35J_cjs = require('./chunk-S367Y35J.cjs');
7
+ var chunkBNXMFD3T_cjs = require('./chunk-BNXMFD3T.cjs');
8
+ require('./chunk-XZDLJ2N6.cjs');
9
+ var chunk254YHUN3_cjs = require('./chunk-254YHUN3.cjs');
7
10
  var react = require('react');
8
11
  var sanity = require('sanity');
9
- var ui = require('@sanity/ui');
10
12
  var jsxRuntime = require('react/jsx-runtime');
13
+ var ui = require('@sanity/ui');
11
14
 
15
+ var GEOChecklist = (props) => {
16
+ var _a;
17
+ const { path, schemaType } = props;
18
+ const [expanded, setExpanded] = react.useState(false);
19
+ const licenseKey = (_a = schemaType.options) == null ? void 0 : _a.licenseKey;
20
+ const seoFields = sanity.useFormValue([path[0]]);
21
+ const rootDoc = sanity.useFormValue([]);
22
+ const freeChecks = react.useMemo(() => {
23
+ var _a2, _b, _c, _d;
24
+ const desc = (seoFields == null ? void 0 : seoFields.description) || "";
25
+ const title = (seoFields == null ? void 0 : seoFields.title) || "";
26
+ return [
27
+ {
28
+ id: "description_answer_ready",
29
+ label: "Description is answer-ready (100\u2013160 chars)",
30
+ pass: !!desc && desc.length >= 100 && desc.length <= 160,
31
+ hint: "Write a concise 100\u2013160 char answer that directly addresses user intent."
32
+ },
33
+ {
34
+ id: "not_noindexed",
35
+ label: "Page is indexable (not noindex)",
36
+ pass: !((_a2 = seoFields == null ? void 0 : seoFields.robots) == null ? void 0 : _a2.noIndex),
37
+ hint: "Page is set to noindex \u2014 AI overviews cannot cite noindexed pages."
38
+ },
39
+ {
40
+ id: "title_concise",
41
+ label: "Title is 30\u201360 characters",
42
+ pass: !!(title && title.length >= 30 && title.length <= 60),
43
+ hint: "Keep your title between 30\u201360 characters for optimal SERP and AI snippet display."
44
+ },
45
+ {
46
+ id: "og_image_present",
47
+ label: "OG or meta image present",
48
+ pass: !!(((_b = seoFields == null ? void 0 : seoFields.metaImage) == null ? void 0 : _b.asset) || ((_d = (_c = seoFields == null ? void 0 : seoFields.openGraph) == null ? void 0 : _c.image) == null ? void 0 : _d.asset)),
49
+ hint: "Add a meta or OG image for rich results and social previews."
50
+ },
51
+ {
52
+ id: "has_keywords",
53
+ label: "Keywords added",
54
+ pass: !!((seoFields == null ? void 0 : seoFields.keywords) && seoFields.keywords.filter((k) => k && k.trim().length > 0).length > 0),
55
+ hint: "Add at least one keyword to improve topical signal for AI classifiers."
56
+ }
57
+ ];
58
+ }, [seoFields]);
59
+ const proChecks = react.useMemo(
60
+ () => licenseKey ? chunkBNXMFD3T_cjs.buildGEOProChecks(seoFields, rootDoc) : null,
61
+ [licenseKey, seoFields, rootDoc]
62
+ );
63
+ const freePassing = freeChecks.filter((c) => c.pass).length;
64
+ const proPassing = proChecks ? proChecks.filter((c) => c.pass).length : 0;
65
+ const totalPassing = freePassing + (proChecks ? proPassing : 0);
66
+ const totalChecks = freeChecks.length + (proChecks ? proChecks.length : 0);
67
+ const percentage = totalChecks > 0 ? totalPassing / totalChecks * 100 : 0;
68
+ let scoreColor = "#dc2626";
69
+ let scoreBg = "#fef2f2";
70
+ let scoreText = "#b91c1c";
71
+ if (percentage >= 80) {
72
+ scoreColor = "#16a34a";
73
+ scoreBg = "#f0fdf4";
74
+ scoreText = "#15803d";
75
+ } else if (percentage >= 50) {
76
+ scoreColor = "#ca8a04";
77
+ scoreBg = "#fefce8";
78
+ scoreText = "#a16207";
79
+ }
80
+ return /* @__PURE__ */ jsxRuntime.jsxs(
81
+ "div",
82
+ {
83
+ style: {
84
+ borderRadius: 10,
85
+ border: "1px solid #e5e7eb",
86
+ overflow: "hidden",
87
+ fontFamily: "inherit"
88
+ },
89
+ children: [
90
+ /* @__PURE__ */ jsxRuntime.jsxs(
91
+ "div",
92
+ {
93
+ style: {
94
+ padding: "16px 20px 14px",
95
+ borderBottom: "1px solid #f3f4f6",
96
+ display: "flex",
97
+ alignItems: "center",
98
+ justifyContent: "space-between",
99
+ gap: 12
100
+ },
101
+ children: [
102
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
103
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "#111827", marginBottom: 2 }, children: "AI Overview Readiness" }),
104
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "#9ca3af" }, children: "Signals that improve visibility in AI-generated overviews" })
105
+ ] }),
106
+ /* @__PURE__ */ jsxRuntime.jsxs(
107
+ "div",
108
+ {
109
+ style: {
110
+ backgroundColor: scoreBg,
111
+ borderRadius: 20,
112
+ padding: "4px 12px",
113
+ display: "flex",
114
+ alignItems: "center",
115
+ gap: 4,
116
+ flexShrink: 0
117
+ },
118
+ children: [
119
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 16, fontWeight: 700, color: scoreColor }, children: totalPassing }),
120
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 12, color: scoreText, opacity: 0.7 }, children: [
121
+ "/ ",
122
+ totalChecks
123
+ ] })
124
+ ]
125
+ }
126
+ )
127
+ ]
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 3, backgroundColor: "#f3f4f6" }, children: /* @__PURE__ */ jsxRuntime.jsx(
131
+ "div",
132
+ {
133
+ style: {
134
+ height: "100%",
135
+ width: `${percentage}%`,
136
+ backgroundColor: scoreColor,
137
+ transition: "width 0.4s ease"
138
+ }
139
+ }
140
+ ) }),
141
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "10px 20px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
142
+ "button",
143
+ {
144
+ type: "button",
145
+ onClick: () => setExpanded((prev) => !prev),
146
+ style: {
147
+ background: "none",
148
+ border: "none",
149
+ padding: 0,
150
+ cursor: "pointer",
151
+ fontSize: 12,
152
+ color: "#6366f1",
153
+ fontWeight: 500,
154
+ display: "flex",
155
+ alignItems: "center",
156
+ gap: 4
157
+ },
158
+ children: [
159
+ /* @__PURE__ */ jsxRuntime.jsx(
160
+ "svg",
161
+ {
162
+ xmlns: "http://www.w3.org/2000/svg",
163
+ width: "12",
164
+ height: "12",
165
+ viewBox: "0 0 24 24",
166
+ fill: "none",
167
+ stroke: "currentColor",
168
+ strokeWidth: "2",
169
+ strokeLinecap: "round",
170
+ strokeLinejoin: "round",
171
+ style: {
172
+ display: "inline-block",
173
+ transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
174
+ transition: "transform 0.2s ease",
175
+ flexShrink: 0
176
+ },
177
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" })
178
+ }
179
+ ),
180
+ expanded ? "Hide checks" : "Show all checks"
181
+ ]
182
+ }
183
+ ) }),
184
+ expanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
185
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "0 20px 16px" }, children: [
186
+ /* @__PURE__ */ jsxRuntime.jsx(
187
+ "div",
188
+ {
189
+ style: {
190
+ fontSize: 10,
191
+ fontWeight: 700,
192
+ letterSpacing: "0.08em",
193
+ color: "#9ca3af",
194
+ marginBottom: 10,
195
+ textTransform: "uppercase"
196
+ },
197
+ children: "Core Checks"
198
+ }
199
+ ),
200
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: freeChecks.map((check) => /* @__PURE__ */ jsxRuntime.jsx(CheckRow, { check }, check.id)) })
201
+ ] }),
202
+ /* @__PURE__ */ jsxRuntime.jsxs(
203
+ "div",
204
+ {
205
+ style: {
206
+ margin: "0 12px 12px",
207
+ borderRadius: 8,
208
+ backgroundColor: licenseKey ? "#faf5ff" : "#f9fafb",
209
+ border: `1px solid ${licenseKey ? "#e9d5ff" : "#f3f4f6"}`,
210
+ overflow: "hidden"
211
+ },
212
+ children: [
213
+ /* @__PURE__ */ jsxRuntime.jsxs(
214
+ "div",
215
+ {
216
+ style: {
217
+ padding: "10px 16px",
218
+ borderBottom: `1px solid ${licenseKey ? "#e9d5ff" : "#f3f4f6"}`,
219
+ display: "flex",
220
+ alignItems: "center",
221
+ gap: 8
222
+ },
223
+ children: [
224
+ /* @__PURE__ */ jsxRuntime.jsx(
225
+ "span",
226
+ {
227
+ style: {
228
+ fontSize: 10,
229
+ fontWeight: 700,
230
+ letterSpacing: "0.08em",
231
+ color: licenseKey ? "#7c3aed" : "#9ca3af",
232
+ textTransform: "uppercase"
233
+ },
234
+ children: "Pro Insights"
235
+ }
236
+ ),
237
+ !licenseKey && /* @__PURE__ */ jsxRuntime.jsx(
238
+ "span",
239
+ {
240
+ style: {
241
+ fontSize: 10,
242
+ backgroundColor: "#f3e8ff",
243
+ color: "#7c3aed",
244
+ borderRadius: 4,
245
+ padding: "1px 6px",
246
+ fontWeight: 600
247
+ },
248
+ children: "Unlock with licenseKey"
249
+ }
250
+ ),
251
+ licenseKey && proChecks && /* @__PURE__ */ jsxRuntime.jsxs(
252
+ "span",
253
+ {
254
+ style: {
255
+ fontSize: 10,
256
+ backgroundColor: "#d8b4fe",
257
+ color: "#5b21b6",
258
+ borderRadius: 4,
259
+ padding: "1px 6px",
260
+ fontWeight: 600
261
+ },
262
+ children: [
263
+ proPassing,
264
+ " / ",
265
+ proChecks.length,
266
+ " passing"
267
+ ]
268
+ }
269
+ )
270
+ ]
271
+ }
272
+ ),
273
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "10px 16px" }, children: proChecks ? /* @__PURE__ */ jsxRuntime.jsx(chunkBNXMFD3T_cjs.GEOChecklistProChecks, { checks: proChecks }) : /* @__PURE__ */ jsxRuntime.jsx(chunkBNXMFD3T_cjs.GEOLockedTeaser, {}) })
274
+ ]
275
+ }
276
+ )
277
+ ] })
278
+ ]
279
+ }
280
+ );
281
+ };
282
+ function CheckRow({ check }) {
283
+ return /* @__PURE__ */ jsxRuntime.jsxs(
284
+ "div",
285
+ {
286
+ style: {
287
+ display: "flex",
288
+ alignItems: "flex-start",
289
+ gap: 10,
290
+ padding: "9px 12px",
291
+ borderRadius: 7,
292
+ backgroundColor: check.pass ? "transparent" : "#fff8f8",
293
+ border: `1px solid ${check.pass ? "transparent" : "#fee2e2"}`
294
+ },
295
+ children: [
296
+ /* @__PURE__ */ jsxRuntime.jsx(
297
+ "div",
298
+ {
299
+ style: {
300
+ width: 20,
301
+ height: 20,
302
+ borderRadius: "50%",
303
+ backgroundColor: check.pass ? "#dcfce7" : "#fee2e2",
304
+ display: "flex",
305
+ alignItems: "center",
306
+ justifyContent: "center",
307
+ flexShrink: 0,
308
+ marginTop: 1
309
+ },
310
+ children: /* @__PURE__ */ jsxRuntime.jsx(
311
+ "span",
312
+ {
313
+ style: {
314
+ fontSize: 10,
315
+ fontWeight: 800,
316
+ color: check.pass ? "#16a34a" : "#dc2626",
317
+ lineHeight: 1
318
+ },
319
+ children: check.pass ? "\u2713" : "\u2717"
320
+ }
321
+ )
322
+ }
323
+ ),
324
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
325
+ /* @__PURE__ */ jsxRuntime.jsx(
326
+ "div",
327
+ {
328
+ style: {
329
+ fontSize: 12,
330
+ fontWeight: 500,
331
+ color: check.pass ? "#374151" : "#1f2937",
332
+ lineHeight: 1.4
333
+ },
334
+ children: check.label
335
+ }
336
+ ),
337
+ !check.pass && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "#9ca3af", marginTop: 3, lineHeight: 1.5 }, children: check.hint })
338
+ ] })
339
+ ]
340
+ }
341
+ );
342
+ }
343
+ var GEOChecklist_default = GEOChecklist;
344
+ var FocusKeywordInput = (props) => {
345
+ var _a;
346
+ const { value, renderDefault, path } = props;
347
+ const parent = sanity.useFormValue([path[0]]);
348
+ const rootDoc = sanity.useFormValue([]);
349
+ const feedbackItems = react.useMemo(() => {
350
+ var _a2, _b;
351
+ if (!value) return [];
352
+ const kw = value.toLowerCase();
353
+ const title = ((parent == null ? void 0 : parent.title) || "").toLowerCase();
354
+ const description = ((parent == null ? void 0 : parent.description) || "").toLowerCase();
355
+ const slug = ((_a2 = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a2.current) || "";
356
+ const items = [];
357
+ if (title) {
358
+ if (title.startsWith(kw)) {
359
+ items.push({ text: "Keyword at start of title \u2014 excellent", color: "green" });
360
+ } else if (title.includes(kw)) {
361
+ items.push({
362
+ text: "Keyword in title \u2014 move it to the start for more prominence",
363
+ color: "orange"
364
+ });
365
+ } else {
366
+ items.push({ text: "Keyword missing from meta title", color: "red" });
367
+ }
368
+ }
369
+ if (description) {
370
+ if (description.includes(kw)) {
371
+ items.push({ text: "Keyword in meta description", color: "green" });
372
+ } else {
373
+ items.push({ text: "Not in description \u2014 include it naturally", color: "orange" });
374
+ }
375
+ }
376
+ if (slug) {
377
+ const kwSlug = kw.replace(/\s+/g, "-");
378
+ if (slug.includes(kwSlug)) {
379
+ items.push({ text: "Keyword in URL slug", color: "green" });
380
+ } else {
381
+ items.push({
382
+ text: "Keyword not in URL slug \u2014 consider it for new pages",
383
+ color: "orange"
384
+ });
385
+ }
386
+ }
387
+ const safeKw = kw.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
388
+ if (((_b = title.match(new RegExp(safeKw, "g"))) != null ? _b : []).length > 2) {
389
+ items.push({
390
+ text: "Keyword appears too many times in title \u2014 avoid stuffing",
391
+ color: "red"
392
+ });
393
+ }
394
+ return items;
395
+ }, [value, parent == null ? void 0 : parent.title, parent == null ? void 0 : parent.description, (_a = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a.current]);
396
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
397
+ renderDefault(props),
398
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
399
+ /* @__PURE__ */ jsxRuntime.jsx(
400
+ "div",
401
+ {
402
+ style: {
403
+ minWidth: 10,
404
+ height: 10,
405
+ borderRadius: "50%",
406
+ backgroundColor: item.color
407
+ }
408
+ }
409
+ ),
410
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
411
+ ] }, item.text)) })
412
+ ] });
413
+ };
414
+ var FocusKeywordInput_default = FocusKeywordInput;
415
+
416
+ // src/utils/readability.ts
417
+ function countSyllables(word) {
418
+ var _a;
419
+ const w = word.toLowerCase().replace(/[^a-z]/g, "");
420
+ if (w.length <= 3) return 1;
421
+ const groups = (_a = w.match(/[aeiouy]+/g)) != null ? _a : [];
422
+ let count = groups.length;
423
+ if (w.endsWith("e") && w.length > 1 && !/[aeiouy]/.test(w[w.length - 2])) {
424
+ count = Math.max(1, count - 1);
425
+ }
426
+ return Math.max(1, count);
427
+ }
428
+ function countSentences(text) {
429
+ const matches = text.match(/[.!?]+/g);
430
+ return matches ? matches.length : 1;
431
+ }
432
+ function analyzeReadability(text) {
433
+ const wordList = text.trim().split(/\s+/).filter(Boolean);
434
+ const words = wordList.length;
435
+ if (words < 10) return null;
436
+ const sentences = countSentences(text);
437
+ const syllables = wordList.reduce((acc, word) => acc + countSyllables(word), 0);
438
+ const rawScore = 206.835 - 1.015 * (words / sentences) - 84.6 * (syllables / words);
439
+ const score = Math.round(rawScore * 10) / 10;
440
+ const rawGrade = 0.39 * (words / sentences) + 11.8 * (syllables / words) - 15.59;
441
+ const gradeLevel = Math.round(rawGrade * 10) / 10;
442
+ const avgWordsPerSentence = Math.round(words / sentences * 10) / 10;
443
+ let label;
444
+ let color;
445
+ if (score >= 70) {
446
+ label = "Easy to read";
447
+ color = "green";
448
+ } else if (score >= 50) {
449
+ label = "Fairly easy";
450
+ color = "green";
451
+ } else if (score >= 30) {
452
+ label = "Moderately difficult";
453
+ color = "orange";
454
+ } else {
455
+ label = "Difficult to read";
456
+ color = "red";
457
+ }
458
+ return {
459
+ score,
460
+ gradeLevel,
461
+ avgWordsPerSentence,
462
+ words,
463
+ sentences,
464
+ label,
465
+ color
466
+ };
467
+ }
12
468
  var MetaDescription = (props) => {
13
469
  const { value, renderDefault, path } = props;
14
470
  const parent = sanity.useFormValue([path[0]]);
@@ -18,6 +474,7 @@ var MetaDescription = (props) => {
18
474
  () => chunkZBHLMQTS_cjs.getMetaDescriptionValidationMessages(value || "", keywords, isParentseoField),
19
475
  [value, keywords, isParentseoField]
20
476
  );
477
+ const readability = react.useMemo(() => analyzeReadability(value || ""), [value]);
21
478
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
22
479
  renderDefault(props),
23
480
  /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
@@ -28,7 +485,30 @@ var MetaDescription = (props) => {
28
485
  }
29
486
  ),
30
487
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
31
- ] }, item.text)) })
488
+ ] }, item.text)) }),
489
+ readability && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
490
+ /* @__PURE__ */ jsxRuntime.jsx(
491
+ "div",
492
+ {
493
+ style: {
494
+ minWidth: 10,
495
+ height: 10,
496
+ borderRadius: "50%",
497
+ backgroundColor: readability.color
498
+ }
499
+ }
500
+ ),
501
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { weight: "bold", muted: true, size: 14, children: [
502
+ "Readability: ",
503
+ readability.label,
504
+ " (Flesch ",
505
+ readability.score,
506
+ " \xB7 Grade",
507
+ " ",
508
+ readability.gradeLevel,
509
+ ")"
510
+ ] })
511
+ ] })
32
512
  ] });
33
513
  };
34
514
  var MetaDescription_default = MetaDescription;
@@ -59,8 +539,204 @@ var MetaImage = (props) => {
59
539
  ] });
60
540
  };
61
541
  var MetaImage_default = MetaImage;
542
+ function resolveImageUrl(asset, projectId, dataset) {
543
+ const ref = asset == null ? void 0 : asset._ref;
544
+ if (!ref) return null;
545
+ const parts = ref.replace("image-", "").split("-");
546
+ const ext = parts[parts.length - 1];
547
+ const dims = parts[parts.length - 2];
548
+ const id = parts.slice(0, parts.length - 2).join("-");
549
+ return `https://cdn.sanity.io/images/${projectId}/${dataset}/${id}-${dims}.${ext}`;
550
+ }
551
+ var MetaTagsPreview = (props) => {
552
+ const { path } = props;
553
+ const [expanded, setExpanded] = react.useState(false);
554
+ const [copyLabel, setCopyLabel] = react.useState("Copy");
555
+ const { scheme } = sanity.useColorScheme();
556
+ const seoFields = sanity.useFormValue([path[0]]);
557
+ const client = sanity.useClient({ apiVersion: "2024-01-01" });
558
+ const { projectId = "", dataset = "" } = client.config();
559
+ const rawHtml = react.useMemo(() => {
560
+ var _a, _b, _c, _d, _e, _f, _g;
561
+ const lines = [];
562
+ lines.push('<meta charset="UTF-8" />');
563
+ lines.push('<meta name="viewport" content="width=device-width, initial-scale=1" />');
564
+ if (seoFields == null ? void 0 : seoFields.title) lines.push(`<title>${seoFields.title}</title>`);
565
+ if (seoFields == null ? void 0 : seoFields.description)
566
+ lines.push(`<meta name="description" content="${seoFields.description}" />`);
567
+ if (seoFields == null ? void 0 : seoFields.robots) {
568
+ const parts = [];
569
+ if (seoFields.robots.noIndex) parts.push("noindex");
570
+ if (seoFields.robots.noFollow) parts.push("nofollow");
571
+ if (seoFields.robots.noTranslate) parts.push("notranslate");
572
+ if (seoFields.robots.noImageIndex) parts.push("noimageindex");
573
+ if (parts.length) lines.push(`<meta name="robots" content="${parts.join(", ")}" />`);
574
+ }
575
+ if ((_a = seoFields == null ? void 0 : seoFields.keywords) == null ? void 0 : _a.length)
576
+ lines.push(`<meta name="keywords" content="${seoFields.keywords.join(", ")}" />`);
577
+ if (seoFields == null ? void 0 : seoFields.canonicalUrl)
578
+ lines.push(`<link rel="canonical" href="${seoFields.canonicalUrl}" />`);
579
+ const og = seoFields == null ? void 0 : seoFields.openGraph;
580
+ if (og == null ? void 0 : og.type) lines.push(`<meta property="og:type" content="${og.type}" />`);
581
+ if (og == null ? void 0 : og.title) lines.push(`<meta property="og:title" content="${og.title}" />`);
582
+ if (og == null ? void 0 : og.description)
583
+ lines.push(`<meta property="og:description" content="${og.description}" />`);
584
+ if (og == null ? void 0 : og.siteName) lines.push(`<meta property="og:site_name" content="${og.siteName}" />`);
585
+ if (og == null ? void 0 : og.url) lines.push(`<meta property="og:url" content="${og.url}" />`);
586
+ const ogImg = (og == null ? void 0 : og.imageUrl) || resolveImageUrl((_b = og == null ? void 0 : og.image) == null ? void 0 : _b.asset, projectId, dataset) || resolveImageUrl((_c = seoFields == null ? void 0 : seoFields.metaImage) == null ? void 0 : _c.asset, projectId, dataset);
587
+ if (ogImg) lines.push(`<meta property="og:image" content="${ogImg}" />`);
588
+ const tw = seoFields == null ? void 0 : seoFields.twitter;
589
+ if (tw == null ? void 0 : tw.card) lines.push(`<meta name="twitter:card" content="${tw.card}" />`);
590
+ if (tw == null ? void 0 : tw.site) lines.push(`<meta name="twitter:site" content="${tw.site}" />`);
591
+ if (tw == null ? void 0 : tw.creator) lines.push(`<meta name="twitter:creator" content="${tw.creator}" />`);
592
+ if (tw == null ? void 0 : tw.title) lines.push(`<meta name="twitter:title" content="${tw.title}" />`);
593
+ if (tw == null ? void 0 : tw.description)
594
+ lines.push(`<meta name="twitter:description" content="${tw.description}" />`);
595
+ const twImg = (tw == null ? void 0 : tw.imageUrl) || resolveImageUrl((_d = tw == null ? void 0 : tw.image) == null ? void 0 : _d.asset, projectId, dataset);
596
+ if (twImg) lines.push(`<meta name="twitter:image" content="${twImg}" />`);
597
+ if ((_e = seoFields == null ? void 0 : seoFields.hreflangs) == null ? void 0 : _e.length) {
598
+ for (const e of seoFields.hreflangs) {
599
+ if (e.locale && e.url)
600
+ lines.push(`<link rel="alternate" hreflang="${e.locale}" href="${e.url}" />`);
601
+ }
602
+ }
603
+ if ((_f = seoFields == null ? void 0 : seoFields.metaAttributes) == null ? void 0 : _f.length) {
604
+ for (const attr of seoFields.metaAttributes) {
605
+ if (attr.key && attr.type === "string" && attr.value) {
606
+ lines.push(`<meta name="${attr.key}" content="${attr.value}" />`);
607
+ } else if (attr.key && attr.type === "image" && ((_g = attr.image) == null ? void 0 : _g.asset)) {
608
+ const imgUrl = resolveImageUrl(attr.image.asset, projectId, dataset);
609
+ if (imgUrl) lines.push(`<meta name="${attr.key}" content="${imgUrl}" />`);
610
+ }
611
+ }
612
+ }
613
+ return lines.join("\n");
614
+ }, [seoFields, projectId, dataset]);
615
+ const tagCount = react.useMemo(() => rawHtml.split("\n").filter(Boolean).length, [rawHtml]);
616
+ const tokens = react.useMemo(() => chunkB5IVI5LP_cjs.tokenizeHtml(rawHtml, scheme), [rawHtml, scheme]);
617
+ const handleCopy = () => {
618
+ navigator.clipboard.writeText(rawHtml).then(() => {
619
+ setCopyLabel("Copied!");
620
+ setTimeout(() => setCopyLabel("Copy"), 2e3);
621
+ });
622
+ };
623
+ const isDark = scheme === "dark";
624
+ const codeBg = isDark ? "#161b22" : "#f6f8fa";
625
+ const codeBorder = isDark ? "#30363d" : "#d0d7de";
626
+ const headerBg = isDark ? "#21262d" : "#f0f2f4";
627
+ const headerBorder = isDark ? "#30363d" : "#d0d7de";
628
+ const headerText = isDark ? "#e6edf3" : "#24292f";
629
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 3, radius: 2, tone: "default", border: true, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
630
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", justify: "space-between", gap: 3, children: [
631
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "semibold", size: 1, children: "Meta Tags Preview" }),
632
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 2, align: "center", children: [
633
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 0, muted: true, children: [
634
+ tagCount,
635
+ " tags generated"
636
+ ] }),
637
+ /* @__PURE__ */ jsxRuntime.jsx(
638
+ ui.Button,
639
+ {
640
+ text: copyLabel,
641
+ tone: "default",
642
+ mode: "ghost",
643
+ fontSize: 1,
644
+ padding: 2,
645
+ onClick: handleCopy
646
+ }
647
+ )
648
+ ] })
649
+ ] }),
650
+ /* @__PURE__ */ jsxRuntime.jsxs(
651
+ "button",
652
+ {
653
+ type: "button",
654
+ onClick: () => setExpanded((p) => !p),
655
+ style: {
656
+ display: "flex",
657
+ alignItems: "center",
658
+ justifyContent: "center",
659
+ gap: 6,
660
+ width: "100%",
661
+ background: headerBg,
662
+ border: `1px solid ${headerBorder}`,
663
+ borderRadius: 6,
664
+ padding: "6px 12px",
665
+ cursor: "pointer",
666
+ fontSize: 12,
667
+ fontWeight: 500,
668
+ color: headerText
669
+ },
670
+ children: [
671
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10 }, children: expanded ? "\u25B2" : "\u25BC" }),
672
+ expanded ? "Hide tags" : "Show tags"
673
+ ]
674
+ }
675
+ ),
676
+ expanded && /* @__PURE__ */ jsxRuntime.jsxs(
677
+ "div",
678
+ {
679
+ style: {
680
+ borderRadius: 6,
681
+ border: `1px solid ${codeBorder}`,
682
+ overflow: "hidden"
683
+ },
684
+ children: [
685
+ /* @__PURE__ */ jsxRuntime.jsxs(
686
+ "div",
687
+ {
688
+ style: {
689
+ background: headerBg,
690
+ borderBottom: `1px solid ${headerBorder}`,
691
+ padding: "6px 14px",
692
+ display: "flex",
693
+ alignItems: "center",
694
+ gap: 6
695
+ },
696
+ children: [
697
+ /* @__PURE__ */ jsxRuntime.jsx(
698
+ "span",
699
+ {
700
+ style: {
701
+ width: 8,
702
+ height: 8,
703
+ borderRadius: "50%",
704
+ background: isDark ? "#388bfd" : "#0550ae",
705
+ display: "inline-block"
706
+ }
707
+ }
708
+ ),
709
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 11, color: headerText, fontFamily: "monospace" }, children: "HTML" })
710
+ ]
711
+ }
712
+ ),
713
+ /* @__PURE__ */ jsxRuntime.jsx(
714
+ ui.Box,
715
+ {
716
+ as: "pre",
717
+ padding: 3,
718
+ style: {
719
+ margin: 0,
720
+ maxHeight: 420,
721
+ overflow: "auto",
722
+ fontSize: 12,
723
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
724
+ lineHeight: 1.7,
725
+ whiteSpace: "pre-wrap",
726
+ wordBreak: "break-word",
727
+ backgroundColor: codeBg
728
+ },
729
+ children: tokens.map((token, idx) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: token.color }, children: token.text }, idx))
730
+ }
731
+ )
732
+ ]
733
+ }
734
+ )
735
+ ] }) });
736
+ };
737
+ var MetaTagsPreview_default = MetaTagsPreview;
62
738
  var MetaTitle = (props) => {
63
- var _a, _b;
739
+ var _a, _b, _c;
64
740
  const { value, renderDefault, path, schemaType } = props;
65
741
  const { options } = schemaType;
66
742
  const parent = sanity.useFormValue([path[0]]);
@@ -69,6 +745,7 @@ var MetaTitle = (props) => {
69
745
  const rootDoc = (_a = sanity.useFormValue([])) != null ? _a : {};
70
746
  const client = sanity.useClient({ apiVersion: (_b = options == null ? void 0 : options.apiVersion) != null ? _b : "2024-01-01" });
71
747
  const [groqTitleSuffix, setGroqTitleSuffix] = react.useState("");
748
+ const [isDuplicateTitle, setIsDuplicateTitle] = react.useState(false);
72
749
  const titleSuffixQuery = options == null ? void 0 : options.titleSuffixQuery;
73
750
  const titleSuffixOption = options == null ? void 0 : options.titleSuffix;
74
751
  react.useEffect(() => {
@@ -79,6 +756,21 @@ var MetaTitle = (props) => {
79
756
  setGroqTitleSuffix("");
80
757
  });
81
758
  }, [titleSuffixQuery, client]);
759
+ const currentDocId = (_c = rootDoc._id) != null ? _c : "";
760
+ react.useEffect(() => {
761
+ if (!(value == null ? void 0 : value.trim()) || !currentDocId) {
762
+ setIsDuplicateTitle(false);
763
+ return () => {
764
+ };
765
+ }
766
+ const timer = setTimeout(() => {
767
+ client.fetch(
768
+ `count(*[seo.title == $title && _id != $id && !(_id in path("drafts.**"))])`,
769
+ { title: value, id: currentDocId }
770
+ ).then((count) => setIsDuplicateTitle(count > 0)).catch(() => setIsDuplicateTitle(false));
771
+ }, 600);
772
+ return () => clearTimeout(timer);
773
+ }, [value, client, currentDocId]);
82
774
  const resolvedSuffix = react.useMemo(() => {
83
775
  if (titleSuffixQuery) return groqTitleSuffix;
84
776
  if (!titleSuffixOption) return "";
@@ -94,20 +786,36 @@ var MetaTitle = (props) => {
94
786
  );
95
787
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
96
788
  renderDefault(props),
97
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
98
- /* @__PURE__ */ jsxRuntime.jsx(
99
- "div",
100
- {
101
- style: {
102
- minWidth: 10,
103
- height: 10,
104
- borderRadius: "50%",
105
- backgroundColor: item.color
789
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
790
+ feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
791
+ /* @__PURE__ */ jsxRuntime.jsx(
792
+ "div",
793
+ {
794
+ style: {
795
+ minWidth: 10,
796
+ height: 10,
797
+ borderRadius: "50%",
798
+ backgroundColor: item.color
799
+ }
106
800
  }
107
- }
108
- ),
109
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
110
- ] }, item.text)) })
801
+ ),
802
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
803
+ ] }, item.text)),
804
+ isDuplicateTitle && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
805
+ /* @__PURE__ */ jsxRuntime.jsx(
806
+ "div",
807
+ {
808
+ style: {
809
+ minWidth: 10,
810
+ height: 10,
811
+ borderRadius: "50%",
812
+ backgroundColor: "orange"
813
+ }
814
+ }
815
+ ),
816
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: "Duplicate meta title \u2014 another published document uses this exact title." })
817
+ ] })
818
+ ] })
111
819
  ] });
112
820
  };
113
821
  var MetaTitle_default = MetaTitle;
@@ -136,7 +844,7 @@ var DEFAULT_FIELD_INFO = {
136
844
  },
137
845
  robots: {
138
846
  title: "Robots Settings",
139
- description: "Configure how search engine crawlers should index and follow links on this page."
847
+ description: "Configure how search engine crawlers should index, follow links, translate content, and index images on this page."
140
848
  },
141
849
  metaAttributes: {
142
850
  title: "Additional Meta Attributes",
@@ -205,6 +913,22 @@ var DEFAULT_FIELD_INFO = {
205
913
  twitterImageUrl: {
206
914
  title: "X Image URL",
207
915
  description: "Enter the full URL of the image for X (formerly Twitter). Ensure the image is accessible and meets the recommended size."
916
+ },
917
+ focusKeyword: {
918
+ title: "Focus Keyword",
919
+ description: "The primary keyword you want this page to rank for. Used to check keyword placement in title, description, and URL."
920
+ },
921
+ hreflangs: {
922
+ title: "Hreflang Entries",
923
+ description: "Add alternate language/region versions of this page. Include x-default for the fallback URL."
924
+ },
925
+ geoChecklist: {
926
+ title: "AI Overview Readiness",
927
+ description: "Checklist of signals that improve visibility in AI-generated overviews."
928
+ },
929
+ metaTagsPreview: {
930
+ title: "Meta Tags Preview",
931
+ description: "Preview the full HTML <head> meta tags generated for this page."
208
932
  }
209
933
  };
210
934
  var getFieldInfo = (fieldName, fieldOverrides) => {
@@ -350,14 +1074,14 @@ function openGraph(config = {}) {
350
1074
  title: "Open Graph Settings",
351
1075
  type: "object",
352
1076
  fields: [
353
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1077
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
354
1078
  name: "url",
355
1079
  type: "url"
356
1080
  }, getFieldInfo("openGraphUrl", config.fieldOverrides)), {
357
1081
  hidden: getFieldHiddenFunction("openGraphUrl", config),
358
1082
  description: "The canonical URL of the page. This should be the full URL including protocol (https://)."
359
1083
  })),
360
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1084
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
361
1085
  name: "title"
362
1086
  }, getFieldInfo("openGraphTitle", config.fieldOverrides)), {
363
1087
  type: "string",
@@ -367,7 +1091,7 @@ function openGraph(config = {}) {
367
1091
  // Can also wrap with a string input + preview
368
1092
  }
369
1093
  })),
370
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1094
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
371
1095
  name: "description"
372
1096
  }, getFieldInfo("openGraphDescription", config.fieldOverrides)), {
373
1097
  type: "text",
@@ -378,13 +1102,13 @@ function openGraph(config = {}) {
378
1102
  // Can also wrap with a text area + preview
379
1103
  }
380
1104
  })),
381
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1105
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
382
1106
  name: "siteName"
383
1107
  }, getFieldInfo("openGraphSiteName", config.fieldOverrides)), {
384
1108
  type: "string",
385
1109
  hidden: getFieldHiddenFunction("openGraphSiteName", config)
386
1110
  })),
387
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1111
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
388
1112
  name: "type"
389
1113
  }, getFieldInfo("openGraphType", config.fieldOverrides)), {
390
1114
  type: "string",
@@ -403,7 +1127,7 @@ function openGraph(config = {}) {
403
1127
  hidden: getFieldHiddenFunction("openGraphType", config),
404
1128
  initialValue: "website"
405
1129
  })),
406
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1130
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
407
1131
  name: "imageType"
408
1132
  }, getFieldInfo("openGraphImageType", config.fieldOverrides)), {
409
1133
  type: "string",
@@ -416,7 +1140,7 @@ function openGraph(config = {}) {
416
1140
  hidden: getFieldHiddenFunction("openGraphImage", config),
417
1141
  initialValue: "upload"
418
1142
  })),
419
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1143
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
420
1144
  name: "image"
421
1145
  }, getFieldInfo("openGraphImage", config.fieldOverrides)), {
422
1146
  type: "image",
@@ -441,7 +1165,7 @@ function openGraph(config = {}) {
441
1165
  return typeof hiddenFn === "function" ? hiddenFn(context) : hiddenFn;
442
1166
  }
443
1167
  })),
444
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1168
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
445
1169
  name: "imageUrl"
446
1170
  }, getFieldInfo("openGraphImageUrl", config.fieldOverrides)), {
447
1171
  type: "url",
@@ -577,7 +1301,7 @@ function twitter(config = {}) {
577
1301
  title: "X (Formerly Twitter)",
578
1302
  type: "object",
579
1303
  fields: [
580
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1304
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
581
1305
  name: "card"
582
1306
  }, getFieldInfo("twitterCard", config.fieldOverrides)), {
583
1307
  type: "string",
@@ -593,19 +1317,19 @@ function twitter(config = {}) {
593
1317
  initialValue: "summary_large_image"
594
1318
  // good default
595
1319
  })),
596
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1320
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
597
1321
  name: "site"
598
1322
  }, getFieldInfo("twitterSite", config.fieldOverrides)), {
599
1323
  type: "string",
600
1324
  hidden: getFieldHiddenFunction("twitterSite", config)
601
1325
  })),
602
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1326
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
603
1327
  name: "creator",
604
1328
  type: "string"
605
1329
  }, getFieldInfo("twitterCreator", config.fieldOverrides)), {
606
1330
  hidden: getFieldHiddenFunction("twitterCreator", config)
607
1331
  })),
608
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1332
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
609
1333
  name: "title",
610
1334
  type: "string"
611
1335
  }, getFieldInfo("twitterTitle", config.fieldOverrides)), {
@@ -614,7 +1338,7 @@ function twitter(config = {}) {
614
1338
  input: twitterTitle_default
615
1339
  }
616
1340
  })),
617
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1341
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
618
1342
  name: "description",
619
1343
  type: "text",
620
1344
  rows: 3
@@ -624,7 +1348,7 @@ function twitter(config = {}) {
624
1348
  input: twitterDescription_default
625
1349
  }
626
1350
  })),
627
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1351
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
628
1352
  name: "imageType"
629
1353
  }, getFieldInfo("twitterImageType", config.fieldOverrides)), {
630
1354
  type: "string",
@@ -637,7 +1361,7 @@ function twitter(config = {}) {
637
1361
  hidden: getFieldHiddenFunction("twitterImage", config),
638
1362
  initialValue: "upload"
639
1363
  })),
640
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1364
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
641
1365
  name: "image"
642
1366
  }, getFieldInfo("twitterImage", config.fieldOverrides)), {
643
1367
  type: "image",
@@ -662,7 +1386,7 @@ function twitter(config = {}) {
662
1386
  return typeof hiddenFn === "function" ? hiddenFn(context) : hiddenFn;
663
1387
  }
664
1388
  })),
665
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1389
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
666
1390
  name: "imageUrl"
667
1391
  }, getFieldInfo("twitterImageUrl", config.fieldOverrides)), {
668
1392
  type: "url",
@@ -681,7 +1405,7 @@ function twitter(config = {}) {
681
1405
  }
682
1406
 
683
1407
  // src/schemas/index.ts
684
- var LazySeoPreview = react.lazy(() => import('./SeoPreview-WYH7NYNM.cjs'));
1408
+ var LazySeoPreview = react.lazy(() => import('./SeoPreview-3EXR6FD4.cjs'));
685
1409
  var SeoPreviewWrapper = (props) => react.createElement(react.Suspense, { fallback: null }, react.createElement(LazySeoPreview, props));
686
1410
  function buildFieldGroupMap(groups) {
687
1411
  const map = /* @__PURE__ */ new Map();
@@ -714,13 +1438,13 @@ function withGroup(field, fieldGroupMap) {
714
1438
  if (!fieldGroupMap) return field;
715
1439
  const groups = fieldGroupMap.get(field.name);
716
1440
  if (!groups || groups.length === 0) return field;
717
- return chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({}, field), { group: groups.length === 1 ? groups[0] : groups });
1441
+ return chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({}, field), { group: groups.length === 1 ? groups[0] : groups });
718
1442
  }
719
1443
  function seoFieldsSchema(config = {}) {
720
1444
  const groupsCfg = config.fieldGroups;
721
1445
  const fieldGroupMap = (groupsCfg == null ? void 0 : groupsCfg.length) ? buildFieldGroupMap(groupsCfg) : void 0;
722
1446
  const sanityGroups = (groupsCfg == null ? void 0 : groupsCfg.length) ? toSanityGroups(groupsCfg) : void 0;
723
- return sanity.defineType(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1447
+ return sanity.defineType(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
724
1448
  name: "seoFields",
725
1449
  title: "SEO Fields",
726
1450
  type: "object"
@@ -743,7 +1467,7 @@ function seoFieldsSchema(config = {}) {
743
1467
  title: "SEO Preview",
744
1468
  type: "string",
745
1469
  components: { input: SeoPreviewWrapper },
746
- options: chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues({
1470
+ options: chunk254YHUN3_cjs.__spreadValues(chunk254YHUN3_cjs.__spreadValues(chunk254YHUN3_cjs.__spreadValues(chunk254YHUN3_cjs.__spreadValues(chunk254YHUN3_cjs.__spreadValues({
747
1471
  baseUrl: config.baseUrl || "https://www.example.com"
748
1472
  }, config.apiVersion ? { apiVersion: config.apiVersion } : {}), typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.prefix ? { prefix: config.seoPreview.prefix } : {}), typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.titleSuffix ? { titleSuffix: config.seoPreview.titleSuffix } : {}), typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.titleSuffixInheritColor ? { titleSuffixInheritColor: config.seoPreview.titleSuffixInheritColor } : {}), typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.titleSuffixQuery ? { titleSuffixQuery: config.seoPreview.titleSuffixQuery } : {}),
749
1473
  initialValue: "",
@@ -753,20 +1477,20 @@ function seoFieldsSchema(config = {}) {
753
1477
  )
754
1478
  ] : [],
755
1479
  withGroup(
756
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1480
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
757
1481
  name: "title"
758
1482
  }, getFieldInfo("title", config.fieldOverrides)), {
759
1483
  type: "string",
760
1484
  components: {
761
1485
  input: MetaTitle_default
762
1486
  },
763
- options: chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues({}, config.apiVersion ? { apiVersion: config.apiVersion } : {}), typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.titleSuffix ? { titleSuffix: config.seoPreview.titleSuffix } : {}), typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.titleSuffixQuery ? { titleSuffixQuery: config.seoPreview.titleSuffixQuery } : {}),
1487
+ options: chunk254YHUN3_cjs.__spreadValues(chunk254YHUN3_cjs.__spreadValues(chunk254YHUN3_cjs.__spreadValues({}, config.apiVersion ? { apiVersion: config.apiVersion } : {}), typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.titleSuffix ? { titleSuffix: config.seoPreview.titleSuffix } : {}), typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.titleSuffixQuery ? { titleSuffixQuery: config.seoPreview.titleSuffixQuery } : {}),
764
1488
  hidden: getFieldHiddenFunction("title", config)
765
1489
  })),
766
1490
  fieldGroupMap
767
1491
  ),
768
1492
  withGroup(
769
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1493
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
770
1494
  name: "description"
771
1495
  }, getFieldInfo("description", config.fieldOverrides)), {
772
1496
  type: "text",
@@ -779,7 +1503,7 @@ function seoFieldsSchema(config = {}) {
779
1503
  fieldGroupMap
780
1504
  ),
781
1505
  withGroup(
782
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1506
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
783
1507
  name: "metaImage"
784
1508
  }, getFieldInfo("metaImage", config.fieldOverrides)), {
785
1509
  type: "image",
@@ -794,7 +1518,7 @@ function seoFieldsSchema(config = {}) {
794
1518
  fieldGroupMap
795
1519
  ),
796
1520
  withGroup(
797
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1521
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
798
1522
  name: "metaAttributes"
799
1523
  }, getFieldInfo("metaAttributes", config.fieldOverrides)), {
800
1524
  type: "array",
@@ -804,7 +1528,7 @@ function seoFieldsSchema(config = {}) {
804
1528
  fieldGroupMap
805
1529
  ),
806
1530
  withGroup(
807
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1531
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
808
1532
  name: "keywords"
809
1533
  }, getFieldInfo("keywords", config.fieldOverrides)), {
810
1534
  title: "Keywords",
@@ -816,7 +1540,7 @@ function seoFieldsSchema(config = {}) {
816
1540
  fieldGroupMap
817
1541
  ),
818
1542
  withGroup(
819
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1543
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
820
1544
  name: "canonicalUrl"
821
1545
  }, getFieldInfo("canonicalUrl", config.fieldOverrides)), {
822
1546
  title: "Canonical URL",
@@ -826,6 +1550,60 @@ function seoFieldsSchema(config = {}) {
826
1550
  })),
827
1551
  fieldGroupMap
828
1552
  ),
1553
+ withGroup(
1554
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
1555
+ name: "focusKeyword"
1556
+ }, getFieldInfo("focusKeyword", config.fieldOverrides)), {
1557
+ type: "string",
1558
+ components: {
1559
+ input: FocusKeywordInput_default
1560
+ },
1561
+ hidden: getFieldHiddenFunction("focusKeyword", config)
1562
+ })),
1563
+ fieldGroupMap
1564
+ ),
1565
+ withGroup(
1566
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
1567
+ name: "hreflangs"
1568
+ }, getFieldInfo("hreflangs", config.fieldOverrides)), {
1569
+ type: "array",
1570
+ of: [{ type: "hreflangEntry" }],
1571
+ description: "Add alternate language/region versions. Include x-default for the fallback URL.",
1572
+ hidden: getFieldHiddenFunction("hreflangs", config)
1573
+ })),
1574
+ fieldGroupMap
1575
+ ),
1576
+ ...config.geo === false ? [] : [
1577
+ withGroup(
1578
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
1579
+ name: "geoChecklist"
1580
+ }, getFieldInfo("geoChecklist", config.fieldOverrides)), {
1581
+ type: "string",
1582
+ readOnly: true,
1583
+ components: {
1584
+ input: GEOChecklist_default
1585
+ },
1586
+ options: chunk254YHUN3_cjs.__spreadValues({}, config.licenseKey ? { licenseKey: config.licenseKey } : {}),
1587
+ hidden: getFieldHiddenFunction("geoChecklist", config)
1588
+ })),
1589
+ fieldGroupMap
1590
+ )
1591
+ ],
1592
+ ...config.metaTagsPreview === false ? [] : [
1593
+ withGroup(
1594
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
1595
+ name: "metaTagsPreview"
1596
+ }, getFieldInfo("metaTagsPreview", config.fieldOverrides)), {
1597
+ type: "string",
1598
+ readOnly: true,
1599
+ components: {
1600
+ input: MetaTagsPreview_default
1601
+ },
1602
+ hidden: getFieldHiddenFunction("metaTagsPreview", config)
1603
+ })),
1604
+ fieldGroupMap
1605
+ )
1606
+ ],
829
1607
  withGroup(openGraph(config), fieldGroupMap),
830
1608
  withGroup(twitter(config), fieldGroupMap)
831
1609
  ]
@@ -837,7 +1615,7 @@ function baseMeta(config = {}) {
837
1615
  title: "Basic Meta Settings",
838
1616
  type: "object",
839
1617
  fields: [
840
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1618
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
841
1619
  name: "title"
842
1620
  }, getFieldInfo("title", config.fieldOverrides)), {
843
1621
  type: "string",
@@ -846,7 +1624,7 @@ function baseMeta(config = {}) {
846
1624
  },
847
1625
  hidden: getFieldHiddenFunction("title", config)
848
1626
  })),
849
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1627
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
850
1628
  name: "description"
851
1629
  }, getFieldInfo("description", config.fieldOverrides)), {
852
1630
  type: "text",
@@ -856,7 +1634,7 @@ function baseMeta(config = {}) {
856
1634
  },
857
1635
  hidden: getFieldHiddenFunction("description", config)
858
1636
  })),
859
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1637
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
860
1638
  name: "metaImage"
861
1639
  }, getFieldInfo("metaImage", config.fieldOverrides)), {
862
1640
  type: "image",
@@ -868,7 +1646,7 @@ function baseMeta(config = {}) {
868
1646
  },
869
1647
  hidden: getFieldHiddenFunction("metaImage", config)
870
1648
  })),
871
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1649
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
872
1650
  name: "keywords"
873
1651
  }, getFieldInfo("keywords", config.fieldOverrides)), {
874
1652
  title: "Keywords",
@@ -877,7 +1655,7 @@ function baseMeta(config = {}) {
877
1655
  description: "Add relevant keywords for this page. These keywords will be used for SEO purposes.",
878
1656
  hidden: getFieldHiddenFunction("keywords", config)
879
1657
  })),
880
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1658
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
881
1659
  name: "canonicalUrl"
882
1660
  }, getFieldInfo("canonicalUrl", config.fieldOverrides)), {
883
1661
  title: "Canonical URL",
@@ -885,7 +1663,7 @@ function baseMeta(config = {}) {
885
1663
  description: "Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.",
886
1664
  hidden: getFieldHiddenFunction("canonicalUrl", config)
887
1665
  })),
888
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1666
+ sanity.defineField(chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
889
1667
  name: "metaAttributes"
890
1668
  }, getFieldInfo("metaAttributes", config.fieldOverrides)), {
891
1669
  type: "array",
@@ -895,6 +1673,38 @@ function baseMeta(config = {}) {
895
1673
  ]
896
1674
  });
897
1675
  }
1676
+ var hreflang_default = sanity.defineType({
1677
+ name: "hreflangEntry",
1678
+ title: "Hreflang Entry",
1679
+ type: "object",
1680
+ fields: [
1681
+ sanity.defineField({
1682
+ name: "locale",
1683
+ title: "Language / Region",
1684
+ type: "string",
1685
+ description: "BCP 47 tag \u2014 e.g. en, fr-FR, x-default",
1686
+ validation: (Rule) => Rule.required().custom((value) => {
1687
+ if (!value) return "Required";
1688
+ if (!/^(x-default|[a-z]{2,3}(-[A-Z]{2,3})?)$/.test(value)) {
1689
+ return "Must be a valid BCP 47 tag (e.g. en, fr-FR, x-default)";
1690
+ }
1691
+ return true;
1692
+ })
1693
+ }),
1694
+ sanity.defineField({
1695
+ name: "url",
1696
+ title: "URL",
1697
+ type: "url",
1698
+ validation: (Rule) => Rule.required().uri({ scheme: ["http", "https"] })
1699
+ })
1700
+ ],
1701
+ preview: {
1702
+ select: {
1703
+ title: "locale",
1704
+ subtitle: "url"
1705
+ }
1706
+ }
1707
+ });
898
1708
  var metaAttribute_default = sanity.defineType({
899
1709
  name: "metaAttribute",
900
1710
  title: "Meta Attribute",
@@ -985,9 +1795,23 @@ var robots_default = sanity.defineType({
985
1795
  type: "boolean",
986
1796
  initialValue: false,
987
1797
  description: "Enable this to prevent search engines from following links on this page. Links will not pass SEO value."
1798
+ }),
1799
+ sanity.defineField({
1800
+ name: "noTranslate",
1801
+ title: "No Translate",
1802
+ type: "boolean",
1803
+ initialValue: false,
1804
+ description: "Enable this to prevent search engines from offering a translated version of this page in search results."
1805
+ }),
1806
+ sanity.defineField({
1807
+ name: "noImageIndex",
1808
+ title: "No Image Index",
1809
+ type: "boolean",
1810
+ initialValue: false,
1811
+ description: "Enable this to prevent images on this page from appearing in image search results."
988
1812
  })
989
1813
  ],
990
- description: "Select how search engines should index and follow links on this page."
1814
+ description: "Select how search engines should index, follow links, translate content, and index images on this page."
991
1815
  });
992
1816
 
993
1817
  // src/schemas/types/index.ts
@@ -1003,13 +1827,14 @@ function types(config = {}) {
1003
1827
  // pass config here
1004
1828
  metaAttribute_default,
1005
1829
  metaTag_default,
1006
- robots_default
1830
+ robots_default,
1831
+ hreflang_default
1007
1832
  ];
1008
1833
  }
1009
1834
 
1010
1835
  // src/plugin.ts
1011
- var resolveDashboardConfig = (healthDashboard, rootApiVersion) => {
1012
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
1836
+ var resolveDashboardConfig = (healthDashboard, rootApiVersion, rootLicenseKey) => {
1837
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
1013
1838
  const cfg = typeof healthDashboard === "object" ? healthDashboard : void 0;
1014
1839
  const deprecationWarnings = [];
1015
1840
  if ((cfg == null ? void 0 : cfg.apiVersion) !== void 0) {
@@ -1022,27 +1847,37 @@ var resolveDashboardConfig = (healthDashboard, rootApiVersion) => {
1022
1847
  '[sanity-plugin-seofields] "healthDashboard.apiVersion" is deprecated. Move it to the root "apiVersion" option instead.'
1023
1848
  );
1024
1849
  }
1850
+ if ((cfg == null ? void 0 : cfg.licenseKey) !== void 0 && !rootLicenseKey) {
1851
+ deprecationWarnings.push({
1852
+ key: "healthDashboard.licenseKey \u2192 licenseKey",
1853
+ version: "v1.8.0",
1854
+ changelogUrl: "https://github.com/hardik-143/sanity-plugin-seofields/blob/main/CHANGELOG.md"
1855
+ });
1856
+ console.warn(
1857
+ '[sanity-plugin-seofields] "healthDashboard.licenseKey" is deprecated. Move it to the root "licenseKey" option instead.'
1858
+ );
1859
+ }
1025
1860
  return {
1026
1861
  enabled: healthDashboard !== false,
1027
- toolTitle: (_b = (_a = cfg == null ? void 0 : cfg.tool) == null ? void 0 : _a.title) != null ? _b : "SEO Health",
1028
- toolName: (_d = (_c = cfg == null ? void 0 : cfg.tool) == null ? void 0 : _c.name) != null ? _d : "seo-health-dashboard",
1029
- icon: (_e = cfg == null ? void 0 : cfg.content) == null ? void 0 : _e.icon,
1030
- title: (_f = cfg == null ? void 0 : cfg.content) == null ? void 0 : _f.title,
1031
- description: (_g = cfg == null ? void 0 : cfg.content) == null ? void 0 : _g.description,
1862
+ toolTitle: (_c = (_b = (_a = cfg == null ? void 0 : cfg.tool) == null ? void 0 : _a.title) != null ? _b : cfg == null ? void 0 : cfg.toolTitle) != null ? _c : "SEO Health",
1863
+ toolName: (_e = (_d = cfg == null ? void 0 : cfg.tool) == null ? void 0 : _d.name) != null ? _e : "seo-health-dashboard",
1864
+ icon: (_f = cfg == null ? void 0 : cfg.content) == null ? void 0 : _f.icon,
1865
+ title: (_g = cfg == null ? void 0 : cfg.content) == null ? void 0 : _g.title,
1866
+ description: (_h = cfg == null ? void 0 : cfg.content) == null ? void 0 : _h.description,
1032
1867
  showTypeColumn: cfg == null ? void 0 : cfg.showTypeColumn,
1033
1868
  showDocumentId: cfg == null ? void 0 : cfg.showDocumentId,
1034
- queryTypes: (_h = cfg == null ? void 0 : cfg.query) == null ? void 0 : _h.types,
1035
- queryRequireSeo: (_i = cfg == null ? void 0 : cfg.query) == null ? void 0 : _i.requireSeo,
1036
- queryGroq: (_j = cfg == null ? void 0 : cfg.query) == null ? void 0 : _j.groq,
1037
- apiVersion: (_k = cfg == null ? void 0 : cfg.apiVersion) != null ? _k : rootApiVersion,
1038
- licenseKey: cfg == null ? void 0 : cfg.licenseKey,
1869
+ queryTypes: (_i = cfg == null ? void 0 : cfg.query) == null ? void 0 : _i.types,
1870
+ queryRequireSeo: (_j = cfg == null ? void 0 : cfg.query) == null ? void 0 : _j.requireSeo,
1871
+ queryGroq: (_k = cfg == null ? void 0 : cfg.query) == null ? void 0 : _k.groq,
1872
+ apiVersion: (_l = cfg == null ? void 0 : cfg.apiVersion) != null ? _l : rootApiVersion,
1873
+ licenseKey: rootLicenseKey != null ? rootLicenseKey : cfg == null ? void 0 : cfg.licenseKey,
1039
1874
  typeDisplayLabels: cfg == null ? void 0 : cfg.typeDisplayLabels,
1040
1875
  typeColumnMode: cfg == null ? void 0 : cfg.typeColumnMode,
1041
1876
  titleField: cfg == null ? void 0 : cfg.titleField,
1042
1877
  getDocumentBadge: cfg == null ? void 0 : cfg.getDocumentBadge,
1043
- loadingLicense: (_l = cfg == null ? void 0 : cfg.content) == null ? void 0 : _l.loadingLicense,
1044
- loadingDocuments: (_m = cfg == null ? void 0 : cfg.content) == null ? void 0 : _m.loadingDocuments,
1045
- noDocuments: (_n = cfg == null ? void 0 : cfg.content) == null ? void 0 : _n.noDocuments,
1878
+ loadingLicense: (_m = cfg == null ? void 0 : cfg.content) == null ? void 0 : _m.loadingLicense,
1879
+ loadingDocuments: (_n = cfg == null ? void 0 : cfg.content) == null ? void 0 : _n.loadingDocuments,
1880
+ noDocuments: (_o = cfg == null ? void 0 : cfg.content) == null ? void 0 : _o.noDocuments,
1046
1881
  previewMode: cfg == null ? void 0 : cfg.previewMode,
1047
1882
  structureTool: cfg == null ? void 0 : cfg.structureTool,
1048
1883
  exportEnabled: (() => {
@@ -1057,14 +1892,15 @@ var resolveDashboardConfig = (healthDashboard, rootApiVersion) => {
1057
1892
  if (typeof exportCfg === "object" && exportCfg.formats) return exportCfg.formats;
1058
1893
  return ["csv", "json"];
1059
1894
  })(),
1060
- compactStats: (_o = cfg == null ? void 0 : cfg.compactStats) != null ? _o : false,
1895
+ compactStats: (_p = cfg == null ? void 0 : cfg.compactStats) != null ? _p : false,
1061
1896
  deprecationWarnings
1062
1897
  };
1063
1898
  };
1064
1899
  var seofields = sanity.definePlugin((config = {}) => {
1065
- const { healthDashboard = true } = config;
1066
- const dash = resolveDashboardConfig(healthDashboard, config.apiVersion);
1067
- const LazySeoHealthTool = react.lazy(() => import('./SeoHealthTool-4VZMD4TR.cjs'));
1900
+ const cfg = config;
1901
+ const { healthDashboard = true, licenseKey, publishGate } = cfg;
1902
+ const dash = resolveDashboardConfig(healthDashboard, cfg.apiVersion, licenseKey);
1903
+ const LazySeoHealthTool = react.lazy(() => import('./SeoHealthTool-2KKQJLJL.cjs'));
1068
1904
  const BoundSeoHealthTool = () => react.createElement(
1069
1905
  react.Suspense,
1070
1906
  { fallback: null },
@@ -1094,12 +1930,23 @@ var seofields = sanity.definePlugin((config = {}) => {
1094
1930
  _deprecationWarnings: dash.deprecationWarnings
1095
1931
  })
1096
1932
  );
1097
- return chunkS367Y35J_cjs.__spreadValues({
1933
+ return chunk254YHUN3_cjs.__spreadValues(chunk254YHUN3_cjs.__spreadValues({
1098
1934
  name: "sanity-plugin-seofields",
1099
1935
  schema: {
1100
- types: types(config)
1936
+ types: types(cfg)
1101
1937
  }
1102
- }, dash.enabled && {
1938
+ }, publishGate && licenseKey && {
1939
+ document: {
1940
+ actions: (prev, context) => {
1941
+ var _a;
1942
+ if (((_a = publishGate.types) == null ? void 0 : _a.length) && !publishGate.types.includes(context.schemaType))
1943
+ return prev;
1944
+ return prev.map(
1945
+ (Action) => Action.action === "publish" ? chunkBNXMFD3T_cjs.createSeoPublishGateAction(Action, chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({}, publishGate), { licenseKey })) : Action
1946
+ );
1947
+ }
1948
+ }
1949
+ }), dash.enabled && {
1103
1950
  tools: [
1104
1951
  {
1105
1952
  name: dash.toolName,
@@ -1111,31 +1958,140 @@ var seofields = sanity.definePlugin((config = {}) => {
1111
1958
  });
1112
1959
  });
1113
1960
  var plugin_default = seofields;
1114
- var LazySeoHealthDashboard = react.lazy(() => import('./SeoHealthDashboard-D3XU3CUB.cjs'));
1115
- function createSeoHealthPane(optionsOrS, optionsWhenS) {
1116
- const S = optionsOrS;
1117
- const _a = optionsWhenS != null ? optionsWhenS : {}, { query, openInPane = true, title: paneTitle } = _a, rest = chunkS367Y35J_cjs.__objRest(_a, ["query", "openInPane", "title"]);
1118
- const SeoHealthPane = () => /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(
1119
- LazySeoHealthDashboard,
1120
- chunkS367Y35J_cjs.__spreadValues({
1121
- customQuery: query,
1122
- openInPane,
1123
- title: paneTitle
1124
- }, rest)
1125
- ) });
1126
- SeoHealthPane.displayName = "SeoHealthPane";
1127
- return S.component(SeoHealthPane).title(paneTitle != null ? paneTitle : "SEO Health").child((docId, { params }) => {
1128
- const builder = S.document().documentId(docId);
1129
- return (params == null ? void 0 : params.type) ? builder.schemaType(params.type) : builder;
1130
- });
1131
- }
1961
+
1962
+ // src/utils/scoring.ts
1963
+ var getStatusCategory = (score) => {
1964
+ if (score >= 80) return "excellent";
1965
+ if (score >= 60) return "good";
1966
+ if (score >= 40) return "fair";
1967
+ if (score > 0) return "poor";
1968
+ return "missing";
1969
+ };
1970
+ var scoreMetaTitle = (title) => {
1971
+ const issues = [];
1972
+ let score = 0;
1973
+ if (title && title.length >= 50 && title.length <= 60) {
1974
+ score = 15;
1975
+ } else if (title && title.length > 0) {
1976
+ score = 10;
1977
+ if (title.length < 50) issues.push("Meta title too short (< 50 chars)");
1978
+ if (title.length > 60) issues.push("Meta title too long (> 60 chars)");
1979
+ } else {
1980
+ issues.push("Missing meta title");
1981
+ }
1982
+ return { score, issues };
1983
+ };
1984
+ var scoreMetaDescription = (description) => {
1985
+ const issues = [];
1986
+ let score = 0;
1987
+ if (description && description.length >= 120 && description.length <= 160) {
1988
+ score = 15;
1989
+ } else if (description && description.length > 0) {
1990
+ score = 10;
1991
+ if (description.length < 120) issues.push("Meta description too short (< 120 chars)");
1992
+ if (description.length > 160) issues.push("Meta description too long (> 160 chars)");
1993
+ } else {
1994
+ issues.push("Missing meta description");
1995
+ }
1996
+ return { score, issues };
1997
+ };
1998
+ var scoreOpenGraph = (openGraph2) => {
1999
+ const issues = [];
2000
+ let score = 0;
2001
+ if (openGraph2) {
2002
+ if (openGraph2.title) score += 6;
2003
+ else issues.push("Missing OG title");
2004
+ if (openGraph2.description) score += 6;
2005
+ else issues.push("Missing OG description");
2006
+ if (openGraph2.image) score += 6;
2007
+ else issues.push("Missing OG image");
2008
+ if (openGraph2.type) score += 7;
2009
+ else issues.push("Missing OG type");
2010
+ } else {
2011
+ issues.push("Open Graph not configured");
2012
+ }
2013
+ return { score, issues };
2014
+ };
2015
+ var scoreTwitterCard = (twitter2) => {
2016
+ const issues = [];
2017
+ let score = 0;
2018
+ if (twitter2) {
2019
+ if (twitter2.title) score += 5;
2020
+ else issues.push("Missing Twitter title");
2021
+ if (twitter2.description) score += 5;
2022
+ else issues.push("Missing Twitter description");
2023
+ if (twitter2.image) score += 5;
2024
+ else issues.push("Missing Twitter image");
2025
+ } else {
2026
+ issues.push("Twitter Card not configured");
2027
+ }
2028
+ return { score, issues };
2029
+ };
2030
+ var calculateHealthScore = (doc) => {
2031
+ if (!doc.seo) {
2032
+ return { score: 0, status: "missing", issues: ["SEO fields not configured"] };
2033
+ }
2034
+ const seo = doc.seo;
2035
+ const title = seo.title;
2036
+ const description = seo.description;
2037
+ const keywords = seo.keywords;
2038
+ const robots = seo.robots;
2039
+ const openGraph2 = seo.openGraph;
2040
+ const twitter2 = seo.twitter;
2041
+ let score = 0;
2042
+ const issues = [];
2043
+ const titleResult = scoreMetaTitle(title);
2044
+ score += titleResult.score;
2045
+ issues.push(...titleResult.issues);
2046
+ const descResult = scoreMetaDescription(description);
2047
+ score += descResult.score;
2048
+ issues.push(...descResult.issues);
2049
+ if (seo.metaImage) score += 10;
2050
+ else issues.push("Missing meta image");
2051
+ if (keywords && keywords.length > 0) score += 10;
2052
+ else issues.push("No keywords defined");
2053
+ if (robots && !robots.noIndex) score += 5;
2054
+ else if (!robots) score += 5;
2055
+ const ogResult = scoreOpenGraph(openGraph2);
2056
+ score += ogResult.score;
2057
+ issues.push(...ogResult.issues);
2058
+ const twResult = scoreTwitterCard(twitter2);
2059
+ score += twResult.score;
2060
+ issues.push(...twResult.issues);
2061
+ const hasMetaImage = !!seo.metaImage;
2062
+ const hasOgImage = !!(openGraph2 && openGraph2.image);
2063
+ const hasTwitterImage = !!(twitter2 && twitter2.image);
2064
+ if (hasMetaImage && hasOgImage && hasTwitterImage) {
2065
+ score += 5;
2066
+ } else {
2067
+ const missingImages = [];
2068
+ if (!hasMetaImage) missingImages.push("meta image");
2069
+ if (!hasOgImage) missingImages.push("OG image");
2070
+ if (!hasTwitterImage) missingImages.push("Twitter image");
2071
+ issues.push(`Missing images for full score: ${missingImages.join(", ")}`);
2072
+ }
2073
+ const status = getStatusCategory(score);
2074
+ return { score, status, issues };
2075
+ };
1132
2076
 
1133
2077
  // src/index.ts
1134
2078
  var src_default = plugin_default;
1135
2079
 
2080
+ Object.defineProperty(exports, "SchemaOrgDocumentJsonLdPreview", {
2081
+ enumerable: true,
2082
+ get: function () { return chunkB5IVI5LP_cjs.SchemaOrgJsonLdPreview; }
2083
+ });
2084
+ Object.defineProperty(exports, "SchemaOrgJsonLdPreview", {
2085
+ enumerable: true,
2086
+ get: function () { return chunkB5IVI5LP_cjs.SchemaOrgJsonLdPreview; }
2087
+ });
2088
+ Object.defineProperty(exports, "createSeoHealthPane", {
2089
+ enumerable: true,
2090
+ get: function () { return chunkBNXMFD3T_cjs.createSeoHealthPane; }
2091
+ });
1136
2092
  exports.allSchemas = types;
1137
2093
  exports.baseMetaSchema = baseMeta;
1138
- exports.createSeoHealthPane = createSeoHealthPane;
2094
+ exports.calculateHealthScore = calculateHealthScore;
1139
2095
  exports.default = src_default;
1140
2096
  exports.metaAttributeSchema = metaAttribute_default;
1141
2097
  exports.metaTagSchema = metaTag_default;