sanity-plugin-seofields 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +440 -29
  2. package/dist/SchemaOrgJsonLdPreview-DJLkIPq4.d.cts +78 -0
  3. package/dist/SchemaOrgJsonLdPreview-DJLkIPq4.d.ts +78 -0
  4. package/dist/SeoHealthTool-2COI27KI.cjs +8 -0
  5. package/dist/SeoHealthTool-2COI27KI.cjs.map +1 -0
  6. package/dist/SeoHealthTool-CWI3KB2V.js +8 -0
  7. package/dist/SeoHealthTool-CWI3KB2V.js.map +1 -0
  8. package/dist/{SeoPreview-WYH7NYNM.cjs → SeoPreview-LMZAWWOE.cjs} +35 -38
  9. package/dist/SeoPreview-LMZAWWOE.cjs.map +1 -0
  10. package/dist/{SeoPreview-KEGGQSPJ.js → SeoPreview-UNQBKTQO.js} +14 -11
  11. package/dist/SeoPreview-UNQBKTQO.js.map +1 -0
  12. package/dist/chunk-3OK3S2F7.cjs +673 -0
  13. package/dist/chunk-3OK3S2F7.cjs.map +1 -0
  14. package/dist/{chunk-GWPOJSGW.cjs → chunk-5XTQRILL.cjs} +1022 -307
  15. package/dist/chunk-5XTQRILL.cjs.map +1 -0
  16. package/dist/{chunk-2NMEKWO5.js → chunk-7N4MLTMR.js} +8 -15
  17. package/dist/chunk-7N4MLTMR.js.map +1 -0
  18. package/dist/{chunk-S367Y35J.cjs → chunk-A57XNQC7.cjs} +9 -20
  19. package/dist/chunk-A57XNQC7.cjs.map +1 -0
  20. package/dist/chunk-BWLJDK5J.js +624 -0
  21. package/dist/chunk-BWLJDK5J.js.map +1 -0
  22. package/dist/chunk-FUUWBQE2.cjs +195 -0
  23. package/dist/chunk-FUUWBQE2.cjs.map +1 -0
  24. package/dist/{chunk-ZBHLMQTS.cjs → chunk-HHO2AKAP.js} +44 -17
  25. package/dist/chunk-HHO2AKAP.js.map +1 -0
  26. package/dist/chunk-KIEO6QG3.js +195 -0
  27. package/dist/chunk-KIEO6QG3.js.map +1 -0
  28. package/dist/chunk-R2U7JF7U.cjs +624 -0
  29. package/dist/chunk-R2U7JF7U.cjs.map +1 -0
  30. package/dist/{chunk-HRKRV2X7.js → chunk-XZKQWV2H.js} +974 -148
  31. package/dist/chunk-XZKQWV2H.js.map +1 -0
  32. package/dist/{chunk-HDZZQCH7.js → chunk-Y46ACXM4.cjs} +45 -5
  33. package/dist/chunk-Y46ACXM4.cjs.map +1 -0
  34. package/dist/chunk-Z5AOUU4H.js +673 -0
  35. package/dist/chunk-Z5AOUU4H.js.map +1 -0
  36. package/dist/cli.js +27 -27
  37. package/dist/{component-DQ4KuQQD.d.ts → component-CjT1hvxh.d.ts} +3 -27
  38. package/dist/{component-D1sBm6ts.d.cts → component-WYh0z8as.d.cts} +3 -27
  39. package/dist/define-cli.cjs +2 -4
  40. package/dist/define-cli.cjs.map +1 -1
  41. package/dist/define-cli.js +4 -4
  42. package/dist/define-cli.js.map +1 -1
  43. package/dist/head.cjs +14 -0
  44. package/dist/head.cjs.map +1 -0
  45. package/dist/head.d.cts +254 -0
  46. package/dist/head.d.ts +254 -0
  47. package/dist/head.js +14 -0
  48. package/dist/head.js.map +1 -0
  49. package/dist/index.cjs +1797 -230
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +11 -598
  52. package/dist/index.d.ts +11 -598
  53. package/dist/index.js +1777 -197
  54. package/dist/index.js.map +1 -1
  55. package/dist/next.cjs +210 -447
  56. package/dist/next.cjs.map +1 -1
  57. package/dist/next.d.cts +8 -200
  58. package/dist/next.d.ts +8 -200
  59. package/dist/next.js +200 -124
  60. package/dist/next.js.map +1 -1
  61. package/dist/plugin-DQdThyxG.d.ts +449 -0
  62. package/dist/plugin-DoGeZP79.d.cts +449 -0
  63. package/dist/schema/next.cjs +173 -319
  64. package/dist/schema/next.cjs.map +1 -1
  65. package/dist/schema/next.d.cts +4 -4
  66. package/dist/schema/next.d.ts +4 -4
  67. package/dist/schema/next.js +172 -8
  68. package/dist/schema/next.js.map +1 -1
  69. package/dist/schema.cjs +390 -489
  70. package/dist/schema.cjs.map +1 -1
  71. package/dist/schema.d.cts +26 -16
  72. package/dist/schema.d.ts +26 -16
  73. package/dist/schema.js +236 -13
  74. package/dist/schema.js.map +1 -1
  75. package/dist/server.cjs +149 -0
  76. package/dist/server.cjs.map +1 -0
  77. package/dist/server.d.cts +102 -0
  78. package/dist/server.d.ts +102 -0
  79. package/dist/server.js +149 -0
  80. package/dist/server.js.map +1 -0
  81. package/dist/{types-CeBULNFF.d.ts → types-BAdN1RMu.d.ts} +2 -2
  82. package/dist/{types-_wBLg6RV.d.cts → types-C5i3KMgs.d.cts} +2 -2
  83. package/dist/{types-9_wbCIi9.d.ts → types-DP-DiW6f.d.cts} +29 -2
  84. package/dist/{types-Bb49oRBV.d.cts → types-DRLUGLtX.d.ts} +29 -2
  85. package/dist/{types-B91ena4g.d.cts → types-DxlPXihz.d.cts} +8 -1
  86. package/dist/{types-B91ena4g.d.ts → types-DxlPXihz.d.ts} +8 -1
  87. package/package.json +25 -3
  88. package/dist/SeoHealthDashboard-IP5BJ322.cjs +0 -10
  89. package/dist/SeoHealthDashboard-IP5BJ322.cjs.map +0 -1
  90. package/dist/SeoHealthDashboard-PTUEVYZL.js +0 -4
  91. package/dist/SeoHealthDashboard-PTUEVYZL.js.map +0 -1
  92. package/dist/SeoHealthTool-CZZRMWQK.cjs +0 -14
  93. package/dist/SeoHealthTool-CZZRMWQK.cjs.map +0 -1
  94. package/dist/SeoHealthTool-IULT4G25.js +0 -12
  95. package/dist/SeoHealthTool-IULT4G25.js.map +0 -1
  96. package/dist/SeoPreview-KEGGQSPJ.js.map +0 -1
  97. package/dist/SeoPreview-WYH7NYNM.cjs.map +0 -1
  98. package/dist/chunk-2NMEKWO5.js.map +0 -1
  99. package/dist/chunk-5YRH47VQ.cjs +0 -2167
  100. package/dist/chunk-5YRH47VQ.cjs.map +0 -1
  101. package/dist/chunk-ARELX3GK.cjs +0 -689
  102. package/dist/chunk-ARELX3GK.cjs.map +0 -1
  103. package/dist/chunk-AW7OKBQQ.js +0 -2161
  104. package/dist/chunk-AW7OKBQQ.js.map +0 -1
  105. package/dist/chunk-FONJYVF2.js +0 -610
  106. package/dist/chunk-FONJYVF2.js.map +0 -1
  107. package/dist/chunk-GWPOJSGW.cjs.map +0 -1
  108. package/dist/chunk-HDZZQCH7.js.map +0 -1
  109. package/dist/chunk-HRKRV2X7.js.map +0 -1
  110. package/dist/chunk-S367Y35J.cjs.map +0 -1
  111. package/dist/chunk-ZBHLMQTS.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,49 +1,922 @@
1
- 'use strict';
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }
2
+
3
+
4
+
5
+ var _chunk3OK3S2F7cjs = require('./chunk-3OK3S2F7.cjs');
6
+
7
+
8
+
9
+ var _chunk5XTQRILLcjs = require('./chunk-5XTQRILL.cjs');
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+ var _chunkY46ACXM4cjs = require('./chunk-Y46ACXM4.cjs');
26
+
27
+
28
+
29
+ var _chunkA57XNQC7cjs = require('./chunk-A57XNQC7.cjs');
30
+
31
+ // src/plugin.ts
32
+ var _react = require('react');
33
+ var _sanity = require('sanity');
34
+
35
+ // src/actions/SeoPublishGateAction.tsx
36
+ var _seofieldspro = require('seofields-pro');
37
+
38
+ // src/schemas/index.ts
39
+
40
+
41
+
42
+
43
+
44
+
45
+ // src/components/geo/GEOChecklist.tsx
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+ var _jsxruntime = require('react/jsx-runtime');
54
+ var GEOChecklist = (props) => {
55
+ var _a;
56
+ const { path, schemaType } = props;
57
+ const [expanded, setExpanded] = _react.useState.call(void 0, false);
58
+ const licenseKey = (_a = schemaType.options) == null ? void 0 : _a.licenseKey;
59
+ const seoFields = _sanity.useFormValue.call(void 0, [path[0]]);
60
+ const rootDoc = _sanity.useFormValue.call(void 0, []);
61
+ const freeChecks = _react.useMemo.call(void 0, () => {
62
+ var _a2, _b, _c, _d;
63
+ const desc = (seoFields == null ? void 0 : seoFields.description) || "";
64
+ const title = (seoFields == null ? void 0 : seoFields.title) || "";
65
+ return [
66
+ {
67
+ id: "description_answer_ready",
68
+ label: "Description is answer-ready (100\u2013160 chars)",
69
+ pass: !!desc && desc.length >= 100 && desc.length <= 160,
70
+ hint: "Write a concise 100\u2013160 char answer that directly addresses user intent."
71
+ },
72
+ {
73
+ id: "not_noindexed",
74
+ label: "Page is indexable (not noindex)",
75
+ pass: !((_a2 = seoFields == null ? void 0 : seoFields.robots) == null ? void 0 : _a2.noIndex),
76
+ hint: "Page is set to noindex \u2014 AI overviews cannot cite noindexed pages."
77
+ },
78
+ {
79
+ id: "title_concise",
80
+ label: "Title is 30\u201360 characters",
81
+ pass: !!(title && title.length >= 30 && title.length <= 60),
82
+ hint: "Keep your title between 30\u201360 characters for optimal SERP and AI snippet display."
83
+ },
84
+ {
85
+ id: "og_image_present",
86
+ label: "OG or meta image present",
87
+ 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)),
88
+ hint: "Add a meta or OG image for rich results and social previews."
89
+ },
90
+ {
91
+ id: "has_keywords",
92
+ label: "Keywords added",
93
+ pass: !!((seoFields == null ? void 0 : seoFields.keywords) && seoFields.keywords.filter((k) => k && k.trim().length > 0).length > 0),
94
+ hint: "Add at least one keyword to improve topical signal for AI classifiers."
95
+ }
96
+ ];
97
+ }, [seoFields]);
98
+ const proChecks = _react.useMemo.call(void 0,
99
+ () => licenseKey ? _seofieldspro.buildGEOProChecks.call(void 0, seoFields, rootDoc) : null,
100
+ [licenseKey, seoFields, rootDoc]
101
+ );
102
+ const freePassing = freeChecks.filter((c) => c.pass).length;
103
+ const proPassing = proChecks ? proChecks.filter((c) => c.pass).length : 0;
104
+ const totalPassing = freePassing + (proChecks ? proPassing : 0);
105
+ const totalChecks = freeChecks.length + (proChecks ? proChecks.length : 0);
106
+ const percentage = totalChecks > 0 ? totalPassing / totalChecks * 100 : 0;
107
+ let scoreColor = "#dc2626";
108
+ let scoreBg = "#fef2f2";
109
+ let scoreText = "#b91c1c";
110
+ if (percentage >= 80) {
111
+ scoreColor = "#16a34a";
112
+ scoreBg = "#f0fdf4";
113
+ scoreText = "#15803d";
114
+ } else if (percentage >= 50) {
115
+ scoreColor = "#ca8a04";
116
+ scoreBg = "#fefce8";
117
+ scoreText = "#a16207";
118
+ }
119
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
120
+ "div",
121
+ {
122
+ style: {
123
+ borderRadius: 10,
124
+ border: "1px solid #e5e7eb",
125
+ overflow: "hidden",
126
+ fontFamily: "inherit"
127
+ },
128
+ children: [
129
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
130
+ "div",
131
+ {
132
+ style: {
133
+ padding: "16px 20px 14px",
134
+ borderBottom: "1px solid #f3f4f6",
135
+ display: "flex",
136
+ alignItems: "center",
137
+ justifyContent: "space-between",
138
+ gap: 12
139
+ },
140
+ children: [
141
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
142
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { style: { fontSize: 13, fontWeight: 600, color: "#111827", marginBottom: 2 }, children: "AI Overview Readiness" }),
143
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { style: { fontSize: 11, color: "#9ca3af" }, children: "Signals that improve visibility in AI-generated overviews" })
144
+ ] }),
145
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
146
+ "div",
147
+ {
148
+ style: {
149
+ backgroundColor: scoreBg,
150
+ borderRadius: 20,
151
+ padding: "4px 12px",
152
+ display: "flex",
153
+ alignItems: "center",
154
+ gap: 4,
155
+ flexShrink: 0
156
+ },
157
+ children: [
158
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { style: { fontSize: 16, fontWeight: 700, color: scoreColor }, children: totalPassing }),
159
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { style: { fontSize: 12, color: scoreText, opacity: 0.7 }, children: [
160
+ "/ ",
161
+ totalChecks
162
+ ] })
163
+ ]
164
+ }
165
+ )
166
+ ]
167
+ }
168
+ ),
169
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { style: { height: 3, backgroundColor: "#f3f4f6" }, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
170
+ "div",
171
+ {
172
+ style: {
173
+ height: "100%",
174
+ width: `${percentage}%`,
175
+ backgroundColor: scoreColor,
176
+ transition: "width 0.4s ease"
177
+ }
178
+ }
179
+ ) }),
180
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { style: { padding: "10px 20px" }, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
181
+ "button",
182
+ {
183
+ type: "button",
184
+ onClick: () => setExpanded((prev) => !prev),
185
+ style: {
186
+ background: "none",
187
+ border: "none",
188
+ padding: 0,
189
+ cursor: "pointer",
190
+ fontSize: 12,
191
+ color: "#6366f1",
192
+ fontWeight: 500,
193
+ display: "flex",
194
+ alignItems: "center",
195
+ gap: 4
196
+ },
197
+ children: [
198
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
199
+ "svg",
200
+ {
201
+ xmlns: "http://www.w3.org/2000/svg",
202
+ width: "12",
203
+ height: "12",
204
+ viewBox: "0 0 24 24",
205
+ fill: "none",
206
+ stroke: "currentColor",
207
+ strokeWidth: "2",
208
+ strokeLinecap: "round",
209
+ strokeLinejoin: "round",
210
+ style: {
211
+ display: "inline-block",
212
+ transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
213
+ transition: "transform 0.2s ease",
214
+ flexShrink: 0
215
+ },
216
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "m9 18 6-6-6-6" })
217
+ }
218
+ ),
219
+ expanded ? "Hide checks" : "Show all checks"
220
+ ]
221
+ }
222
+ ) }),
223
+ expanded && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
224
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { padding: "0 20px 16px" }, children: [
225
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
226
+ "div",
227
+ {
228
+ style: {
229
+ fontSize: 10,
230
+ fontWeight: 700,
231
+ letterSpacing: "0.08em",
232
+ color: "#9ca3af",
233
+ marginBottom: 10,
234
+ textTransform: "uppercase"
235
+ },
236
+ children: "Core Checks"
237
+ }
238
+ ),
239
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: freeChecks.map((check) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CheckRow, { check }, check.id)) })
240
+ ] }),
241
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
242
+ "div",
243
+ {
244
+ style: {
245
+ margin: "0 12px 12px",
246
+ borderRadius: 8,
247
+ backgroundColor: licenseKey ? "#faf5ff" : "#f9fafb",
248
+ border: `1px solid ${licenseKey ? "#e9d5ff" : "#f3f4f6"}`,
249
+ overflow: "hidden"
250
+ },
251
+ children: [
252
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
253
+ "div",
254
+ {
255
+ style: {
256
+ padding: "10px 16px",
257
+ borderBottom: `1px solid ${licenseKey ? "#e9d5ff" : "#f3f4f6"}`,
258
+ display: "flex",
259
+ alignItems: "center",
260
+ gap: 8
261
+ },
262
+ children: [
263
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
264
+ "span",
265
+ {
266
+ style: {
267
+ fontSize: 10,
268
+ fontWeight: 700,
269
+ letterSpacing: "0.08em",
270
+ color: licenseKey ? "#7c3aed" : "#9ca3af",
271
+ textTransform: "uppercase"
272
+ },
273
+ children: "Pro Insights"
274
+ }
275
+ ),
276
+ !licenseKey && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
277
+ "span",
278
+ {
279
+ style: {
280
+ fontSize: 10,
281
+ backgroundColor: "#f3e8ff",
282
+ color: "#7c3aed",
283
+ borderRadius: 4,
284
+ padding: "1px 6px",
285
+ fontWeight: 600
286
+ },
287
+ children: "Unlock with licenseKey"
288
+ }
289
+ ),
290
+ licenseKey && proChecks && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
291
+ "span",
292
+ {
293
+ style: {
294
+ fontSize: 10,
295
+ backgroundColor: "#d8b4fe",
296
+ color: "#5b21b6",
297
+ borderRadius: 4,
298
+ padding: "1px 6px",
299
+ fontWeight: 600
300
+ },
301
+ children: [
302
+ proPassing,
303
+ " / ",
304
+ proChecks.length,
305
+ " passing"
306
+ ]
307
+ }
308
+ )
309
+ ]
310
+ }
311
+ ),
312
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { style: { padding: "10px 16px" }, children: proChecks ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _seofieldspro.GEOChecklistProChecks, { checks: proChecks }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _seofieldspro.GEOLockedTeaser, {}) })
313
+ ]
314
+ }
315
+ )
316
+ ] })
317
+ ]
318
+ }
319
+ );
320
+ };
321
+ function CheckRow({ check }) {
322
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
323
+ "div",
324
+ {
325
+ style: {
326
+ display: "flex",
327
+ alignItems: "flex-start",
328
+ gap: 10,
329
+ padding: "9px 12px",
330
+ borderRadius: 7,
331
+ backgroundColor: check.pass ? "transparent" : "#fff8f8",
332
+ border: `1px solid ${check.pass ? "transparent" : "#fee2e2"}`
333
+ },
334
+ children: [
335
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
336
+ "div",
337
+ {
338
+ style: {
339
+ width: 20,
340
+ height: 20,
341
+ borderRadius: "50%",
342
+ backgroundColor: check.pass ? "#dcfce7" : "#fee2e2",
343
+ display: "flex",
344
+ alignItems: "center",
345
+ justifyContent: "center",
346
+ flexShrink: 0,
347
+ marginTop: 1
348
+ },
349
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
350
+ "span",
351
+ {
352
+ style: {
353
+ fontSize: 10,
354
+ fontWeight: 800,
355
+ color: check.pass ? "#16a34a" : "#dc2626",
356
+ lineHeight: 1
357
+ },
358
+ children: check.pass ? "\u2713" : "\u2717"
359
+ }
360
+ )
361
+ }
362
+ ),
363
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { flex: 1, minWidth: 0 }, children: [
364
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
365
+ "div",
366
+ {
367
+ style: {
368
+ fontSize: 12,
369
+ fontWeight: 500,
370
+ color: check.pass ? "#374151" : "#1f2937",
371
+ lineHeight: 1.4
372
+ },
373
+ children: check.label
374
+ }
375
+ ),
376
+ !check.pass && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { style: { fontSize: 11, color: "#9ca3af", marginTop: 3, lineHeight: 1.5 }, children: check.hint })
377
+ ] })
378
+ ]
379
+ }
380
+ );
381
+ }
382
+ var GEOChecklist_default = GEOChecklist;
383
+
384
+ // src/components/meta/FocusKeywordInput.tsx
385
+ var _ui = require('@sanity/ui');
386
+
387
+
388
+
389
+ // src/components/ai/SeoGenButton.tsx
390
+ var _icons = require('@sanity/icons');
391
+
392
+
393
+
394
+
395
+ // src/utils/extractDocText.ts
396
+ function extractBlockText(block) {
397
+ if (block._type === "block" && Array.isArray(block.children)) {
398
+ return block.children.map((s) => {
399
+ var _a;
400
+ return (_a = s.text) != null ? _a : "";
401
+ }).join("");
402
+ }
403
+ const parts = [];
404
+ for (const key of Object.keys(block)) {
405
+ if (key === "_type" || key === "_key") continue;
406
+ const val = block[key];
407
+ if (typeof val === "string" && val.trim()) parts.push(val.trim());
408
+ else if (Array.isArray(val)) parts.push(extractArrayText(val));
409
+ }
410
+ return parts.filter(Boolean).join(" ");
411
+ }
412
+ function extractArrayText(arr) {
413
+ return arr.map((b) => extractBlockText(b)).filter(Boolean).join("\n");
414
+ }
415
+ function extractFieldText(value) {
416
+ if (typeof value === "string") return value;
417
+ if (!Array.isArray(value)) return "";
418
+ return extractArrayText(value).replace(/\n{3,}/g, "\n\n").trim();
419
+ }
420
+ function extractDocText(doc, bodyField) {
421
+ const fields = [bodyField].flat();
422
+ const text = fields.map((f) => extractFieldText(doc[f])).filter(Boolean).join("\n\n");
423
+ return text.slice(0, 4e3);
424
+ }
425
+ function resolveContentFields(content, docType) {
426
+ var _a;
427
+ if (!content || typeof content === "string" || Array.isArray(content)) return content != null ? content : "body";
428
+ const key = docType && docType in content ? docType : "default";
429
+ return (_a = content[key]) != null ? _a : "body";
430
+ }
431
+
432
+ // src/components/ai/SeoGenButton.tsx
433
+
434
+ function SeoGenButton({
435
+ field,
436
+ ai,
437
+ seoFieldPath,
438
+ onGenerate
439
+ }) {
440
+ var _a, _b, _c, _d, _e, _f, _g;
441
+ const [state, setState] = _react.useState.call(void 0, "idle");
442
+ const [errorMsg, setErrorMsg] = _react.useState.call(void 0, null);
443
+ const abortRef = _react.useRef.call(void 0, null);
444
+ const cooldownRef = _react.useRef.call(void 0, null);
445
+ const rootDoc = _sanity.useFormValue.call(void 0, []);
446
+ const workspace = _sanity.useWorkspace.call(void 0, );
447
+ const seoObj = _sanity.useFormValue.call(void 0, [seoFieldPath]);
448
+ const focusKeyword = (_a = seoObj == null ? void 0 : seoObj.focusKeyword) != null ? _a : "";
449
+ const keywords = (_b = seoObj == null ? void 0 : seoObj.keywords) != null ? _b : [];
450
+ const bodyField = resolveContentFields(ai == null ? void 0 : ai.content, rootDoc == null ? void 0 : rootDoc._type);
451
+ const content = rootDoc ? extractDocText(rootDoc, bodyField) : "";
452
+ const meta = field === "focusKeyword" ? {
453
+ title: (_c = seoObj == null ? void 0 : seoObj.title) != null ? _c : void 0,
454
+ description: (_d = seoObj == null ? void 0 : seoObj.description) != null ? _d : void 0,
455
+ slug: (_f = (_e = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _e.current) != null ? _f : void 0
456
+ } : void 0;
457
+ const handleGenerate = _react.useCallback.call(void 0, async () => {
458
+ var _a2;
459
+ if (!ai) return;
460
+ (_a2 = abortRef.current) == null ? void 0 : _a2.abort();
461
+ if (cooldownRef.current) clearTimeout(cooldownRef.current);
462
+ setState("loading");
463
+ setErrorMsg(null);
464
+ try {
465
+ let result;
466
+ if (ai.testMode) {
467
+ await new Promise((r) => setTimeout(r, 400));
468
+ result = _chunk3OK3S2F7cjs.pickTestOutput.call(void 0, field);
469
+ } else {
470
+ const controller = new AbortController();
471
+ abortRef.current = controller;
472
+ result = await _chunk3OK3S2F7cjs.generateSeoText.call(void 0, field, content, focusKeyword, keywords, {
473
+ config: _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, ai), { _projectId: workspace.projectId }),
474
+ signal: controller.signal,
475
+ meta
476
+ });
477
+ }
478
+ onGenerate(result);
479
+ setState("cooldown");
480
+ cooldownRef.current = setTimeout(() => setState("idle"), 2e3);
481
+ } catch (err) {
482
+ if (err.name === "AbortError") {
483
+ setState("idle");
484
+ return;
485
+ }
486
+ setErrorMsg(err instanceof Error ? err.message : "Generation failed.");
487
+ setState("error");
488
+ }
489
+ }, [ai, field, content, focusKeyword, keywords, meta, onGenerate, workspace.projectId]);
490
+ _react.useEffect.call(void 0, () => {
491
+ return () => {
492
+ var _a2;
493
+ (_a2 = abortRef.current) == null ? void 0 : _a2.abort();
494
+ if (cooldownRef.current) clearTimeout(cooldownRef.current);
495
+ };
496
+ }, []);
497
+ const isConfigured = Boolean(
498
+ (ai == null ? void 0 : ai.apiKey) || (ai == null ? void 0 : ai.endpoint) || (ai == null ? void 0 : ai.testMode) || (ai == null ? void 0 : ai.provider) === "ollama"
499
+ );
500
+ if (!isConfigured) return null;
501
+ const isLoading = state === "loading";
502
+ const isTestMode = Boolean(ai == null ? void 0 : ai.testMode);
503
+ let buttonText = "Generate with AI";
504
+ if (isLoading) buttonText = "Generating\u2026";
505
+ else if (isTestMode) buttonText = "Generate (test)";
506
+ const isFullWidth = ((_g = ai == null ? void 0 : ai.buttonWidth) != null ? _g : "full") === "full";
507
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Flex, { direction: "column", gap: 1, align: isFullWidth ? "stretch" : "flex-start", children: [
508
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
509
+ _ui.Button,
510
+ {
511
+ mode: "ghost",
512
+ tone: "primary",
513
+ paddingX: isFullWidth ? 2 : 3,
514
+ paddingY: 3,
515
+ fontSize: 1,
516
+ icon: _icons.SparklesIcon,
517
+ text: buttonText,
518
+ onClick: handleGenerate,
519
+ disabled: isLoading || state === "cooldown"
520
+ }
521
+ ),
522
+ !content && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Text, { size: 0, muted: true, children: [
523
+ "(Set ",
524
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "code", { children: "ai.content" }),
525
+ " in plugin config for better results)"
526
+ ] }),
527
+ state === "error" && errorMsg && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Card, { padding: 2, tone: "critical", radius: 2, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { size: 1, children: errorMsg }) })
528
+ ] });
529
+ }
530
+
531
+ // src/components/meta/FocusKeywordInput.tsx
532
+
533
+ var FocusKeywordInput = (props) => {
534
+ var _a;
535
+ const { value, renderDefault, path, schemaType, onChange } = props;
536
+ const { options } = schemaType;
537
+ const parent = _sanity.useFormValue.call(void 0, [path[0]]);
538
+ const rootDoc = _sanity.useFormValue.call(void 0, []);
539
+ const feedbackItems = _react.useMemo.call(void 0, () => {
540
+ var _a2;
541
+ if (!value) return [];
542
+ const title = (parent == null ? void 0 : parent.title) || "";
543
+ const description = (parent == null ? void 0 : parent.description) || "";
544
+ const slug = ((_a2 = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a2.current) || "";
545
+ const placement = _chunkY46ACXM4cjs.getFocusKeywordPlacement.call(void 0, value, title, description);
546
+ const items = [];
547
+ if (title) {
548
+ if (placement.atStartOfTitle) {
549
+ items.push({ text: "Keyword at start of title \u2014 excellent", color: "green" });
550
+ } else if (placement.inTitle) {
551
+ items.push({
552
+ text: "Keyword in title \u2014 move it to the start for more prominence",
553
+ color: "orange"
554
+ });
555
+ } else {
556
+ items.push({ text: "Keyword missing from meta title", color: "red" });
557
+ }
558
+ }
559
+ if (description) {
560
+ if (placement.inDescription) {
561
+ items.push({ text: "Keyword in meta description", color: "green" });
562
+ } else {
563
+ items.push({ text: "Not in description \u2014 include it naturally", color: "orange" });
564
+ }
565
+ }
566
+ if (slug) {
567
+ const kwSlug = value.toLowerCase().replace(/\s+/g, "-");
568
+ if (slug.includes(kwSlug)) {
569
+ items.push({ text: "Keyword in URL slug", color: "green" });
570
+ } else {
571
+ items.push({
572
+ text: "Keyword not in URL slug \u2014 consider it for new pages",
573
+ color: "orange"
574
+ });
575
+ }
576
+ }
577
+ if (placement.isStuffed) {
578
+ items.push({
579
+ text: "Keyword appears too many times in title \u2014 avoid stuffing",
580
+ color: "red"
581
+ });
582
+ }
583
+ return items;
584
+ }, [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]);
585
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
586
+ renderDefault(props),
587
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
588
+ SeoGenButton,
589
+ {
590
+ field: "focusKeyword",
591
+ ai: options == null ? void 0 : options.ai,
592
+ seoFieldPath: String(path[0]),
593
+ onGenerate: (v) => onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, v)))
594
+ }
595
+ ),
596
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
597
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
598
+ "div",
599
+ {
600
+ style: {
601
+ minWidth: 10,
602
+ height: 10,
603
+ borderRadius: "50%",
604
+ backgroundColor: item.color
605
+ }
606
+ }
607
+ ),
608
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
609
+ ] }, item.text)) })
610
+ ] });
611
+ };
612
+ var FocusKeywordInput_default = FocusKeywordInput;
613
+
614
+ // src/components/meta/KeywordsInput.tsx
615
+
616
+
617
+
618
+ // src/components/ai/KeywordSuggestions.tsx
619
+
620
+
621
+
622
+
623
+
624
+ function KeywordSuggestions({
625
+ ai,
626
+ seoFieldPath,
627
+ existing,
628
+ onAdd,
629
+ onAddAll
630
+ }) {
631
+ var _a;
632
+ const [panelState, setPanelState] = _react.useState.call(void 0, "idle");
633
+ const [suggestions, setSuggestions] = _react.useState.call(void 0, []);
634
+ const [errorMsg, setErrorMsg] = _react.useState.call(void 0, null);
635
+ const abortRef = _react.useRef.call(void 0, null);
636
+ const rootDoc = _sanity.useFormValue.call(void 0, []);
637
+ const workspace = _sanity.useWorkspace.call(void 0, );
638
+ const seoObj = _sanity.useFormValue.call(void 0, [seoFieldPath]);
639
+ const focusKeyword = (_a = seoObj == null ? void 0 : seoObj.focusKeyword) != null ? _a : "";
640
+ const bodyField = resolveContentFields(ai == null ? void 0 : ai.content, rootDoc == null ? void 0 : rootDoc._type);
641
+ const content = rootDoc ? extractDocText(rootDoc, bodyField) : "";
642
+ const handleSuggest = _react.useCallback.call(void 0, async () => {
643
+ var _a2;
644
+ if (!ai) return;
645
+ (_a2 = abortRef.current) == null ? void 0 : _a2.abort();
646
+ abortRef.current = new AbortController();
647
+ setPanelState("loading");
648
+ setErrorMsg(null);
649
+ setSuggestions([]);
650
+ try {
651
+ let csv;
652
+ if (ai.testMode) {
653
+ await new Promise((r) => setTimeout(r, 400));
654
+ csv = _chunk3OK3S2F7cjs.pickTestOutput.call(void 0, "keywords");
655
+ } else {
656
+ csv = await _chunk3OK3S2F7cjs.generateSeoText.call(void 0, "keywords", content, focusKeyword, existing, {
657
+ config: _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, ai), { _projectId: workspace.projectId }),
658
+ signal: abortRef.current.signal
659
+ });
660
+ }
661
+ const parsed = csv.split(",").map((k) => k.replace(/^["']|["']$/g, "").trim()).filter(Boolean);
662
+ if (parsed.length === 0) {
663
+ setErrorMsg("No keywords returned. Try again.");
664
+ setPanelState("error");
665
+ return;
666
+ }
667
+ setSuggestions(parsed);
668
+ setPanelState("suggestions");
669
+ } catch (err) {
670
+ if (err.name === "AbortError") {
671
+ setPanelState("idle");
672
+ return;
673
+ }
674
+ setErrorMsg(err instanceof Error ? err.message : "Keyword generation failed.");
675
+ setPanelState("error");
676
+ }
677
+ }, [ai, content, focusKeyword, existing, workspace.projectId]);
678
+ const handleAdd = _react.useCallback.call(void 0,
679
+ (kw) => {
680
+ onAdd(kw);
681
+ setSuggestions((prev) => prev.filter((s) => s !== kw));
682
+ if (suggestions.length <= 1) setPanelState("idle");
683
+ },
684
+ [onAdd, suggestions.length]
685
+ );
686
+ const handleAddAll = _react.useCallback.call(void 0, () => {
687
+ const toAdd = suggestions.filter((kw) => !existing.includes(kw));
688
+ if (toAdd.length > 0) onAddAll(toAdd);
689
+ setSuggestions([]);
690
+ setPanelState("idle");
691
+ }, [suggestions, existing, onAddAll]);
692
+ const handleDismiss = _react.useCallback.call(void 0, () => {
693
+ var _a2;
694
+ (_a2 = abortRef.current) == null ? void 0 : _a2.abort();
695
+ setSuggestions([]);
696
+ setPanelState("idle");
697
+ setErrorMsg(null);
698
+ }, []);
699
+ _react.useEffect.call(void 0, () => {
700
+ return () => {
701
+ var _a2;
702
+ (_a2 = abortRef.current) == null ? void 0 : _a2.abort();
703
+ };
704
+ }, []);
705
+ const isConfigured = Boolean(
706
+ (ai == null ? void 0 : ai.apiKey) || (ai == null ? void 0 : ai.endpoint) || (ai == null ? void 0 : ai.testMode) || (ai == null ? void 0 : ai.provider) === "ollama"
707
+ );
708
+ if (!isConfigured) return null;
709
+ if (panelState === "idle" || panelState === "error") {
710
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 2, children: [
711
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
712
+ _ui.Button,
713
+ {
714
+ mode: "ghost",
715
+ tone: "primary",
716
+ paddingX: 2,
717
+ paddingY: 3,
718
+ fontSize: 1,
719
+ icon: _icons.SparklesIcon,
720
+ text: (ai == null ? void 0 : ai.testMode) ? "Suggest Keywords (test)" : "Suggest Keywords with AI",
721
+ onClick: handleSuggest
722
+ }
723
+ ),
724
+ panelState === "error" && errorMsg && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Card, { padding: 2, tone: "critical", radius: 2, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { size: 1, children: errorMsg }) })
725
+ ] });
726
+ }
727
+ if (panelState === "loading") {
728
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Flex, { align: "center", gap: 2, paddingY: 2, children: [
729
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Spinner, { muted: true }),
730
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { size: 1, muted: true, children: "Finding keywords\u2026" })
731
+ ] });
732
+ }
733
+ const pendingSuggestions = suggestions.filter((kw) => !existing.includes(kw));
734
+ const alreadyAdded = suggestions.filter((kw) => existing.includes(kw));
735
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Card, { padding: 3, radius: 2, tone: "transparent", border: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
736
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Flex, { align: "center", justify: "space-between", children: [
737
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { size: 1, weight: "semibold", muted: true, children: "AI keyword suggestions" }),
738
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Flex, { gap: 2, children: [
739
+ pendingSuggestions.length > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
740
+ _ui.Button,
741
+ {
742
+ mode: "ghost",
743
+ tone: "primary",
744
+ paddingX: 2,
745
+ paddingY: 2,
746
+ fontSize: 1,
747
+ text: "Add all",
748
+ onClick: handleAddAll
749
+ }
750
+ ),
751
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
752
+ _ui.Button,
753
+ {
754
+ mode: "ghost",
755
+ tone: "default",
756
+ paddingX: 2,
757
+ paddingY: 2,
758
+ fontSize: 1,
759
+ text: "Dismiss",
760
+ onClick: handleDismiss
761
+ }
762
+ )
763
+ ] })
764
+ ] }),
765
+ pendingSuggestions.length > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Box, { children: [
766
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { size: 0, muted: true, style: { marginBottom: 8 }, children: "Click to add:" }),
767
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Flex, { gap: 2, wrap: "wrap", style: { marginTop: 6 }, children: pendingSuggestions.map((kw) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
768
+ "button",
769
+ {
770
+ type: "button",
771
+ onClick: () => handleAdd(kw),
772
+ style: {
773
+ padding: "4px 10px",
774
+ borderRadius: 20,
775
+ border: "1px solid var(--card-border-color)",
776
+ background: "transparent",
777
+ color: "var(--card-fg-color)",
778
+ cursor: "pointer",
779
+ fontSize: 12,
780
+ lineHeight: 1.4,
781
+ transition: "background 0.15s"
782
+ },
783
+ onMouseEnter: (e) => {
784
+ ;
785
+ e.currentTarget.style.background = "var(--card-border-color)";
786
+ },
787
+ onMouseLeave: (e) => {
788
+ ;
789
+ e.currentTarget.style.background = "transparent";
790
+ },
791
+ children: [
792
+ "+ ",
793
+ kw
794
+ ]
795
+ },
796
+ kw
797
+ )) })
798
+ ] }),
799
+ alreadyAdded.length > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Box, { children: [
800
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { size: 0, muted: true, style: { marginBottom: 6 }, children: "Already added:" }),
801
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Flex, { gap: 2, wrap: "wrap", style: { marginTop: 4 }, children: alreadyAdded.map((kw) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Badge, { tone: "positive", radius: 5, fontSize: 0, padding: 2, children: [
802
+ "\u2713 ",
803
+ kw
804
+ ] }, kw)) })
805
+ ] }),
806
+ pendingSuggestions.length === 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { size: 1, muted: true, children: "All suggested keywords added." })
807
+ ] }) });
808
+ }
809
+
810
+ // src/components/meta/KeywordsInput.tsx
811
+
812
+ var KeywordsInput = (props) => {
813
+ const { value, renderDefault, path, schemaType, onChange } = props;
814
+ const { options } = schemaType;
815
+ const existing = value != null ? value : [];
816
+ const handleAdd = (kw) => {
817
+ if (existing.includes(kw)) return;
818
+ onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, [...existing, kw])));
819
+ };
820
+ const handleAddAll = (keywords) => {
821
+ const merged = [.../* @__PURE__ */ new Set([...existing, ...keywords])];
822
+ onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, merged)));
823
+ };
824
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
825
+ renderDefault(props),
826
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
827
+ KeywordSuggestions,
828
+ {
829
+ ai: options == null ? void 0 : options.ai,
830
+ seoFieldPath: String(path[0]),
831
+ existing,
832
+ onAdd: handleAdd,
833
+ onAddAll: handleAddAll
834
+ }
835
+ )
836
+ ] });
837
+ };
838
+ var KeywordsInput_default = KeywordsInput;
839
+
840
+ // src/components/meta/MetaDescription.tsx
841
+
2
842
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
843
 
5
- var chunkZBHLMQTS_cjs = require('./chunk-ZBHLMQTS.cjs');
6
- var chunkS367Y35J_cjs = require('./chunk-S367Y35J.cjs');
7
- var react = require('react');
8
- var sanity = require('sanity');
9
- var ui = require('@sanity/ui');
10
- var jsxRuntime = require('react/jsx-runtime');
11
844
 
12
845
  var MetaDescription = (props) => {
13
- const { value, renderDefault, path } = props;
14
- const parent = sanity.useFormValue([path[0]]);
846
+ const { value, renderDefault, path, schemaType, onChange } = props;
847
+ const { options } = schemaType;
848
+ const parent = _sanity.useFormValue.call(void 0, [path[0]]);
15
849
  const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
16
- const keywords = react.useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
17
- const feedbackItems = react.useMemo(
18
- () => chunkZBHLMQTS_cjs.getMetaDescriptionValidationMessages(value || "", keywords, isParentseoField),
850
+ const keywords = _react.useMemo.call(void 0, () => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
851
+ const feedbackItems = _react.useMemo.call(void 0,
852
+ () => _chunkY46ACXM4cjs.getMetaDescriptionValidationMessages.call(void 0, value || "", keywords, isParentseoField),
19
853
  [value, keywords, isParentseoField]
20
854
  );
21
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
855
+ const readability = _react.useMemo.call(void 0, () => _chunk3OK3S2F7cjs.analyzeReadability.call(void 0, value || ""), [value]);
856
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
22
857
  renderDefault(props),
23
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
24
- /* @__PURE__ */ jsxRuntime.jsx(
858
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
859
+ SeoGenButton,
860
+ {
861
+ field: "description",
862
+ ai: options == null ? void 0 : options.ai,
863
+ seoFieldPath: String(path[0]),
864
+ onGenerate: (v) => onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, v)))
865
+ }
866
+ ),
867
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
868
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
25
869
  "div",
26
870
  {
27
871
  style: { width: 10, height: 10, borderRadius: "50%", backgroundColor: item.color }
28
872
  }
29
873
  ),
30
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
31
- ] }, item.text)) })
874
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
875
+ ] }, item.text)) }),
876
+ readability && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
877
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
878
+ "div",
879
+ {
880
+ style: {
881
+ minWidth: 10,
882
+ height: 10,
883
+ borderRadius: "50%",
884
+ backgroundColor: readability.color
885
+ }
886
+ }
887
+ ),
888
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: [
889
+ "Readability: ",
890
+ readability.label,
891
+ " (Flesch ",
892
+ readability.score,
893
+ " \xB7 Grade",
894
+ " ",
895
+ readability.gradeLevel,
896
+ ")"
897
+ ] })
898
+ ] })
32
899
  ] });
33
900
  };
34
901
  var MetaDescription_default = MetaDescription;
902
+
903
+ // src/components/meta/MetaImage.tsx
904
+
905
+
906
+
907
+
35
908
  var MetaImage = (props) => {
36
909
  const { value, renderDefault, path } = props;
37
- const seoParent = sanity.useFormValue([path[0]]);
910
+ const seoParent = _sanity.useFormValue.call(void 0, [path[0]]);
38
911
  const hasImage = !!(value == null ? void 0 : value.asset);
39
- const feedbackItems = react.useMemo(
40
- () => chunkZBHLMQTS_cjs.getMetaImageValidation(hasImage, seoParent),
912
+ const feedbackItems = _react.useMemo.call(void 0,
913
+ () => _chunkY46ACXM4cjs.getMetaImageValidation.call(void 0, hasImage, seoParent),
41
914
  [hasImage, seoParent]
42
915
  );
43
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
916
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
44
917
  renderDefault(props),
45
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
46
- /* @__PURE__ */ jsxRuntime.jsx(
918
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
919
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
47
920
  "div",
48
921
  {
49
922
  style: {
@@ -54,24 +927,233 @@ var MetaImage = (props) => {
54
927
  }
55
928
  }
56
929
  ),
57
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
930
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
58
931
  ] }, item.text)) })
59
932
  ] });
60
933
  };
61
934
  var MetaImage_default = MetaImage;
935
+
936
+ // src/components/meta/MetaTagsPreview.tsx
937
+
938
+
939
+
940
+
941
+ function resolveImageUrl(asset, projectId, dataset) {
942
+ const ref = asset == null ? void 0 : asset._ref;
943
+ if (!ref) return null;
944
+ const parts = ref.replace("image-", "").split("-");
945
+ const ext = parts[parts.length - 1];
946
+ const dims = parts[parts.length - 2];
947
+ const id = parts.slice(0, parts.length - 2).join("-");
948
+ return `https://cdn.sanity.io/images/${projectId}/${dataset}/${id}-${dims}.${ext}`;
949
+ }
950
+ var MetaTagsPreview = (props) => {
951
+ const { path } = props;
952
+ const [expanded, setExpanded] = _react.useState.call(void 0, false);
953
+ const [copyLabel, setCopyLabel] = _react.useState.call(void 0, "Copy");
954
+ const { scheme } = _sanity.useColorScheme.call(void 0, );
955
+ const seoFields = _sanity.useFormValue.call(void 0, [path[0]]);
956
+ const client = _sanity.useClient.call(void 0, { apiVersion: "2024-01-01" });
957
+ const { projectId = "", dataset = "" } = client.config();
958
+ const rawHtml = _react.useMemo.call(void 0, () => {
959
+ var _a, _b, _c, _d, _e, _f, _g;
960
+ const lines = [];
961
+ lines.push('<meta charset="UTF-8" />');
962
+ lines.push('<meta name="viewport" content="width=device-width, initial-scale=1" />');
963
+ if (seoFields == null ? void 0 : seoFields.title) lines.push(`<title>${seoFields.title}</title>`);
964
+ if (seoFields == null ? void 0 : seoFields.description)
965
+ lines.push(`<meta name="description" content="${seoFields.description}" />`);
966
+ if (seoFields == null ? void 0 : seoFields.robots) {
967
+ const parts = [];
968
+ if (seoFields.robots.noIndex) parts.push("noindex");
969
+ if (seoFields.robots.noFollow) parts.push("nofollow");
970
+ if (seoFields.robots.noTranslate) parts.push("notranslate");
971
+ if (seoFields.robots.noImageIndex) parts.push("noimageindex");
972
+ if (parts.length) lines.push(`<meta name="robots" content="${parts.join(", ")}" />`);
973
+ }
974
+ if ((_a = seoFields == null ? void 0 : seoFields.keywords) == null ? void 0 : _a.length)
975
+ lines.push(`<meta name="keywords" content="${seoFields.keywords.join(", ")}" />`);
976
+ if (seoFields == null ? void 0 : seoFields.canonicalUrl)
977
+ lines.push(`<link rel="canonical" href="${seoFields.canonicalUrl}" />`);
978
+ const og = seoFields == null ? void 0 : seoFields.openGraph;
979
+ if (og == null ? void 0 : og.type) lines.push(`<meta property="og:type" content="${og.type}" />`);
980
+ if (og == null ? void 0 : og.title) lines.push(`<meta property="og:title" content="${og.title}" />`);
981
+ if (og == null ? void 0 : og.description)
982
+ lines.push(`<meta property="og:description" content="${og.description}" />`);
983
+ if (og == null ? void 0 : og.siteName) lines.push(`<meta property="og:site_name" content="${og.siteName}" />`);
984
+ if (og == null ? void 0 : og.url) lines.push(`<meta property="og:url" content="${og.url}" />`);
985
+ 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);
986
+ if (ogImg) lines.push(`<meta property="og:image" content="${ogImg}" />`);
987
+ const tw = seoFields == null ? void 0 : seoFields.twitter;
988
+ if (tw == null ? void 0 : tw.card) lines.push(`<meta name="twitter:card" content="${tw.card}" />`);
989
+ if (tw == null ? void 0 : tw.site) lines.push(`<meta name="twitter:site" content="${tw.site}" />`);
990
+ if (tw == null ? void 0 : tw.creator) lines.push(`<meta name="twitter:creator" content="${tw.creator}" />`);
991
+ if (tw == null ? void 0 : tw.title) lines.push(`<meta name="twitter:title" content="${tw.title}" />`);
992
+ if (tw == null ? void 0 : tw.description)
993
+ lines.push(`<meta name="twitter:description" content="${tw.description}" />`);
994
+ const twImg = (tw == null ? void 0 : tw.imageUrl) || resolveImageUrl((_d = tw == null ? void 0 : tw.image) == null ? void 0 : _d.asset, projectId, dataset);
995
+ if (twImg) lines.push(`<meta name="twitter:image" content="${twImg}" />`);
996
+ if ((_e = seoFields == null ? void 0 : seoFields.hreflangs) == null ? void 0 : _e.length) {
997
+ for (const e of seoFields.hreflangs) {
998
+ if (e.locale && e.url)
999
+ lines.push(`<link rel="alternate" hreflang="${e.locale}" href="${e.url}" />`);
1000
+ }
1001
+ }
1002
+ if ((_f = seoFields == null ? void 0 : seoFields.metaAttributes) == null ? void 0 : _f.length) {
1003
+ for (const attr of seoFields.metaAttributes) {
1004
+ if (attr.key && attr.type === "string" && attr.value) {
1005
+ lines.push(`<meta name="${attr.key}" content="${attr.value}" />`);
1006
+ } else if (attr.key && attr.type === "image" && ((_g = attr.image) == null ? void 0 : _g.asset)) {
1007
+ const imgUrl = resolveImageUrl(attr.image.asset, projectId, dataset);
1008
+ if (imgUrl) lines.push(`<meta name="${attr.key}" content="${imgUrl}" />`);
1009
+ }
1010
+ }
1011
+ }
1012
+ return lines.join("\n");
1013
+ }, [seoFields, projectId, dataset]);
1014
+ const tagCount = _react.useMemo.call(void 0, () => rawHtml.split("\n").filter(Boolean).length, [rawHtml]);
1015
+ const tokens = _react.useMemo.call(void 0, () => _chunk5XTQRILLcjs.tokenizeHtml.call(void 0, rawHtml, scheme), [rawHtml, scheme]);
1016
+ const handleCopy = () => {
1017
+ navigator.clipboard.writeText(rawHtml).then(() => {
1018
+ setCopyLabel("Copied!");
1019
+ setTimeout(() => setCopyLabel("Copy"), 2e3);
1020
+ });
1021
+ };
1022
+ const isDark = scheme === "dark";
1023
+ const codeBg = isDark ? "#161b22" : "#f6f8fa";
1024
+ const codeBorder = isDark ? "#30363d" : "#d0d7de";
1025
+ const headerBg = isDark ? "#21262d" : "#f0f2f4";
1026
+ const headerBorder = isDark ? "#30363d" : "#d0d7de";
1027
+ const headerText = isDark ? "#e6edf3" : "#24292f";
1028
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Card, { padding: 3, radius: 2, tone: "default", border: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
1029
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Flex, { align: "center", justify: "space-between", gap: 3, children: [
1030
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "semibold", size: 1, children: "Meta Tags Preview" }),
1031
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Flex, { gap: 2, align: "center", children: [
1032
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Text, { size: 0, muted: true, children: [
1033
+ tagCount,
1034
+ " tags generated"
1035
+ ] }),
1036
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1037
+ _ui.Button,
1038
+ {
1039
+ text: copyLabel,
1040
+ tone: "default",
1041
+ mode: "ghost",
1042
+ fontSize: 1,
1043
+ padding: 2,
1044
+ onClick: handleCopy
1045
+ }
1046
+ )
1047
+ ] })
1048
+ ] }),
1049
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
1050
+ "button",
1051
+ {
1052
+ type: "button",
1053
+ onClick: () => setExpanded((p) => !p),
1054
+ style: {
1055
+ display: "flex",
1056
+ alignItems: "center",
1057
+ justifyContent: "center",
1058
+ gap: 6,
1059
+ width: "100%",
1060
+ background: headerBg,
1061
+ border: `1px solid ${headerBorder}`,
1062
+ borderRadius: 6,
1063
+ padding: "6px 12px",
1064
+ cursor: "pointer",
1065
+ fontSize: 12,
1066
+ fontWeight: 500,
1067
+ color: headerText
1068
+ },
1069
+ children: [
1070
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { style: { fontSize: 10 }, children: expanded ? "\u25B2" : "\u25BC" }),
1071
+ expanded ? "Hide tags" : "Show tags"
1072
+ ]
1073
+ }
1074
+ ),
1075
+ expanded && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
1076
+ "div",
1077
+ {
1078
+ style: {
1079
+ borderRadius: 6,
1080
+ border: `1px solid ${codeBorder}`,
1081
+ overflow: "hidden"
1082
+ },
1083
+ children: [
1084
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
1085
+ "div",
1086
+ {
1087
+ style: {
1088
+ background: headerBg,
1089
+ borderBottom: `1px solid ${headerBorder}`,
1090
+ padding: "6px 14px",
1091
+ display: "flex",
1092
+ alignItems: "center",
1093
+ gap: 6
1094
+ },
1095
+ children: [
1096
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1097
+ "span",
1098
+ {
1099
+ style: {
1100
+ width: 8,
1101
+ height: 8,
1102
+ borderRadius: "50%",
1103
+ background: isDark ? "#388bfd" : "#0550ae",
1104
+ display: "inline-block"
1105
+ }
1106
+ }
1107
+ ),
1108
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { style: { fontSize: 11, color: headerText, fontFamily: "monospace" }, children: "HTML" })
1109
+ ]
1110
+ }
1111
+ ),
1112
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1113
+ _ui.Box,
1114
+ {
1115
+ as: "pre",
1116
+ padding: 3,
1117
+ style: {
1118
+ margin: 0,
1119
+ maxHeight: 420,
1120
+ overflow: "auto",
1121
+ fontSize: 12,
1122
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
1123
+ lineHeight: 1.7,
1124
+ whiteSpace: "pre-wrap",
1125
+ wordBreak: "break-word",
1126
+ backgroundColor: codeBg
1127
+ },
1128
+ children: tokens.map((token, idx) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { style: { color: token.color }, children: token.text }, idx))
1129
+ }
1130
+ )
1131
+ ]
1132
+ }
1133
+ )
1134
+ ] }) });
1135
+ };
1136
+ var MetaTagsPreview_default = MetaTagsPreview;
1137
+
1138
+ // src/components/meta/MetaTitle.tsx
1139
+
1140
+
1141
+
1142
+
62
1143
  var MetaTitle = (props) => {
63
- var _a, _b;
64
- const { value, renderDefault, path, schemaType } = props;
1144
+ var _a, _b, _c;
1145
+ const { value, renderDefault, path, schemaType, onChange } = props;
65
1146
  const { options } = schemaType;
66
- const parent = sanity.useFormValue([path[0]]);
1147
+ const parent = _sanity.useFormValue.call(void 0, [path[0]]);
67
1148
  const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
68
- const keywords = react.useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
69
- const rootDoc = (_a = sanity.useFormValue([])) != null ? _a : {};
70
- const client = sanity.useClient({ apiVersion: (_b = options == null ? void 0 : options.apiVersion) != null ? _b : "2024-01-01" });
71
- const [groqTitleSuffix, setGroqTitleSuffix] = react.useState("");
1149
+ const keywords = _react.useMemo.call(void 0, () => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
1150
+ const rootDoc = (_a = _sanity.useFormValue.call(void 0, [])) != null ? _a : {};
1151
+ const client = _sanity.useClient.call(void 0, { apiVersion: (_b = options == null ? void 0 : options.apiVersion) != null ? _b : "2024-01-01" });
1152
+ const [groqTitleSuffix, setGroqTitleSuffix] = _react.useState.call(void 0, "");
1153
+ const [isDuplicateTitle, setIsDuplicateTitle] = _react.useState.call(void 0, false);
72
1154
  const titleSuffixQuery = options == null ? void 0 : options.titleSuffixQuery;
73
1155
  const titleSuffixOption = options == null ? void 0 : options.titleSuffix;
74
- react.useEffect(() => {
1156
+ _react.useEffect.call(void 0, () => {
75
1157
  if (!titleSuffixQuery) return;
76
1158
  client.fetch(titleSuffixQuery).then((result) => {
77
1159
  setGroqTitleSuffix(result === null || result === void 0 ? "" : String(result));
@@ -79,7 +1161,22 @@ var MetaTitle = (props) => {
79
1161
  setGroqTitleSuffix("");
80
1162
  });
81
1163
  }, [titleSuffixQuery, client]);
82
- const resolvedSuffix = react.useMemo(() => {
1164
+ const currentDocId = (_c = rootDoc._id) != null ? _c : "";
1165
+ _react.useEffect.call(void 0, () => {
1166
+ if (!(value == null ? void 0 : value.trim()) || !currentDocId) {
1167
+ setIsDuplicateTitle(false);
1168
+ return () => {
1169
+ };
1170
+ }
1171
+ const timer = setTimeout(() => {
1172
+ client.fetch(
1173
+ `count(*[seo.title == $title && _id != $id && !(_id in path("drafts.**"))])`,
1174
+ { title: value, id: currentDocId }
1175
+ ).then((count) => setIsDuplicateTitle(count > 0)).catch(() => setIsDuplicateTitle(false));
1176
+ }, 600);
1177
+ return () => clearTimeout(timer);
1178
+ }, [value, client, currentDocId]);
1179
+ const resolvedSuffix = _react.useMemo.call(void 0, () => {
83
1180
  if (titleSuffixQuery) return groqTitleSuffix;
84
1181
  if (!titleSuffixOption) return "";
85
1182
  if (typeof titleSuffixOption === "function") {
@@ -88,31 +1185,60 @@ var MetaTitle = (props) => {
88
1185
  return titleSuffixOption;
89
1186
  }, [titleSuffixQuery, groqTitleSuffix, titleSuffixOption, rootDoc]);
90
1187
  const suffixLength = resolvedSuffix ? resolvedSuffix.length + 3 : 0;
91
- const feedbackItems = react.useMemo(
92
- () => chunkZBHLMQTS_cjs.getMetaTitleValidationMessages(value || "", keywords, isParentseoField, suffixLength),
1188
+ const feedbackItems = _react.useMemo.call(void 0,
1189
+ () => _chunkY46ACXM4cjs.getMetaTitleValidationMessages.call(void 0, value || "", keywords, isParentseoField, suffixLength),
93
1190
  [value, keywords, isParentseoField, suffixLength]
94
1191
  );
95
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1192
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
96
1193
  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
1194
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1195
+ SeoGenButton,
1196
+ {
1197
+ field: "title",
1198
+ ai: options == null ? void 0 : options.ai,
1199
+ seoFieldPath: String(path[0]),
1200
+ onGenerate: (v) => onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, v)))
1201
+ }
1202
+ ),
1203
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 2, children: [
1204
+ feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1205
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1206
+ "div",
1207
+ {
1208
+ style: {
1209
+ minWidth: 10,
1210
+ height: 10,
1211
+ borderRadius: "50%",
1212
+ backgroundColor: item.color
1213
+ }
106
1214
  }
107
- }
108
- ),
109
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
110
- ] }, item.text)) })
1215
+ ),
1216
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1217
+ ] }, item.text)),
1218
+ isDuplicateTitle && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1219
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1220
+ "div",
1221
+ {
1222
+ style: {
1223
+ minWidth: 10,
1224
+ height: 10,
1225
+ borderRadius: "50%",
1226
+ backgroundColor: "orange"
1227
+ }
1228
+ }
1229
+ ),
1230
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: "Duplicate meta title \u2014 another published document uses this exact title." })
1231
+ ] })
1232
+ ] })
111
1233
  ] });
112
1234
  };
113
1235
  var MetaTitle_default = MetaTitle;
114
1236
 
115
1237
  // src/utils/fieldsUtils.ts
1238
+ function withLicense(ai, licenseKey) {
1239
+ if (!ai) return void 0;
1240
+ return licenseKey ? _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, ai), { _licenseKey: licenseKey }) : ai;
1241
+ }
116
1242
  var DEFAULT_FIELD_INFO = {
117
1243
  title: {
118
1244
  title: "Meta Title",
@@ -136,7 +1262,7 @@ var DEFAULT_FIELD_INFO = {
136
1262
  },
137
1263
  robots: {
138
1264
  title: "Robots Settings",
139
- description: "Configure how search engine crawlers should index and follow links on this page."
1265
+ description: "Configure how search engine crawlers should index, follow links, translate content, and index images on this page."
140
1266
  },
141
1267
  metaAttributes: {
142
1268
  title: "Additional Meta Attributes",
@@ -205,6 +1331,22 @@ var DEFAULT_FIELD_INFO = {
205
1331
  twitterImageUrl: {
206
1332
  title: "X Image URL",
207
1333
  description: "Enter the full URL of the image for X (formerly Twitter). Ensure the image is accessible and meets the recommended size."
1334
+ },
1335
+ focusKeyword: {
1336
+ title: "Focus Keyword",
1337
+ description: "The primary keyword you want this page to rank for. Used to check keyword placement in title, description, and URL."
1338
+ },
1339
+ hreflangs: {
1340
+ title: "Hreflang Entries",
1341
+ description: "Add alternate language/region versions of this page. Include x-default for the fallback URL."
1342
+ },
1343
+ geoChecklist: {
1344
+ title: "AI Overview Readiness",
1345
+ description: "Checklist of signals that improve visibility in AI-generated overviews."
1346
+ },
1347
+ metaTagsPreview: {
1348
+ title: "Meta Tags Preview",
1349
+ description: "Preview the full HTML <head> meta tags generated for this page."
208
1350
  }
209
1351
  };
210
1352
  var getFieldInfo = (fieldName, fieldOverrides) => {
@@ -234,19 +1376,38 @@ var getFieldHiddenFunction = (fieldName, config) => {
234
1376
  var isEmpty = (value) => {
235
1377
  return value === null || value === void 0 || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.length === 0 || typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
236
1378
  };
1379
+
1380
+ // src/schemas/types/openGraph/index.ts
1381
+
1382
+
1383
+ // src/components/openGraph/OgDescription.tsx
1384
+
1385
+
1386
+
1387
+
237
1388
  var OgDescription = (props) => {
238
- const { value, renderDefault, path } = props;
239
- const parent = sanity.useFormValue([path[0]]);
1389
+ const { value, renderDefault, path, schemaType, onChange } = props;
1390
+ const { options } = schemaType;
1391
+ const parent = _sanity.useFormValue.call(void 0, [path[0]]);
240
1392
  const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
241
- const keywords = react.useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
242
- const feedbackItems = react.useMemo(
243
- () => chunkZBHLMQTS_cjs.getOgDescriptionValidation(value || "", keywords, isParentseoField),
1393
+ const keywords = _react.useMemo.call(void 0, () => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
1394
+ const feedbackItems = _react.useMemo.call(void 0,
1395
+ () => _chunkY46ACXM4cjs.getOgDescriptionValidation.call(void 0, value || "", keywords, isParentseoField),
244
1396
  [value, keywords, isParentseoField]
245
1397
  );
246
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1398
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
247
1399
  renderDefault(props),
248
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
249
- /* @__PURE__ */ jsxRuntime.jsx(
1400
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1401
+ SeoGenButton,
1402
+ {
1403
+ field: "ogDescription",
1404
+ ai: options == null ? void 0 : options.ai,
1405
+ seoFieldPath: String(path[0]),
1406
+ onGenerate: (v) => onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, v)))
1407
+ }
1408
+ ),
1409
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1410
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
250
1411
  "div",
251
1412
  {
252
1413
  style: {
@@ -257,25 +1418,31 @@ var OgDescription = (props) => {
257
1418
  }
258
1419
  }
259
1420
  ),
260
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1421
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
261
1422
  ] }, item.text)) })
262
1423
  ] });
263
1424
  };
264
1425
  var OgDescription_default = OgDescription;
1426
+
1427
+ // src/components/openGraph/OgImage.tsx
1428
+
1429
+
1430
+
1431
+
265
1432
  var OgImage = (props) => {
266
1433
  const { value, renderDefault, path } = props;
267
- const seoParent = sanity.useFormValue([path[0]]);
1434
+ const seoParent = _sanity.useFormValue.call(void 0, [path[0]]);
268
1435
  const imgValue = value;
269
1436
  const hasImage = !!(imgValue == null ? void 0 : imgValue.asset);
270
1437
  const altText = imgValue == null ? void 0 : imgValue.alt;
271
- const feedbackItems = react.useMemo(
272
- () => chunkZBHLMQTS_cjs.getOgImageValidation(hasImage, altText, seoParent),
1438
+ const feedbackItems = _react.useMemo.call(void 0,
1439
+ () => _chunkY46ACXM4cjs.getOgImageValidation.call(void 0, hasImage, altText, seoParent),
273
1440
  [hasImage, altText, seoParent]
274
1441
  );
275
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1442
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
276
1443
  renderDefault(props),
277
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
278
- /* @__PURE__ */ jsxRuntime.jsx(
1444
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1445
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
279
1446
  "div",
280
1447
  {
281
1448
  style: {
@@ -286,19 +1453,25 @@ var OgImage = (props) => {
286
1453
  }
287
1454
  }
288
1455
  ),
289
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1456
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
290
1457
  ] }, item.text)) })
291
1458
  ] });
292
1459
  };
293
1460
  var OgImage_default = OgImage;
1461
+
1462
+ // src/components/openGraph/OgImageUrl.tsx
1463
+
1464
+
1465
+
1466
+
294
1467
  var OgImageUrl = (props) => {
295
1468
  const { value, renderDefault, path } = props;
296
- const seoParent = sanity.useFormValue([path[0]]);
297
- const feedbackItems = react.useMemo(() => chunkZBHLMQTS_cjs.getOgImageUrlValidation(value, seoParent), [value, seoParent]);
298
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1469
+ const seoParent = _sanity.useFormValue.call(void 0, [path[0]]);
1470
+ const feedbackItems = _react.useMemo.call(void 0, () => _chunkY46ACXM4cjs.getOgImageUrlValidation.call(void 0, value, seoParent), [value, seoParent]);
1471
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
299
1472
  renderDefault(props),
300
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
301
- /* @__PURE__ */ jsxRuntime.jsx(
1473
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1474
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
302
1475
  "div",
303
1476
  {
304
1477
  style: {
@@ -309,24 +1482,40 @@ var OgImageUrl = (props) => {
309
1482
  }
310
1483
  }
311
1484
  ),
312
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1485
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
313
1486
  ] }, item.text)) })
314
1487
  ] });
315
1488
  };
316
1489
  var OgImageUrl_default = OgImageUrl;
1490
+
1491
+ // src/components/openGraph/OgTitle.tsx
1492
+
1493
+
1494
+
1495
+
317
1496
  var OgTitle = (props) => {
318
- const { value, renderDefault, path } = props;
319
- const parent = sanity.useFormValue([path[0]]);
1497
+ const { value, renderDefault, path, schemaType, onChange } = props;
1498
+ const { options } = schemaType;
1499
+ const parent = _sanity.useFormValue.call(void 0, [path[0]]);
320
1500
  const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
321
- const keywords = react.useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
322
- const feedbackItems = react.useMemo(
323
- () => chunkZBHLMQTS_cjs.getOgTitleValidation(value || "", keywords, isParentseoField),
1501
+ const keywords = _react.useMemo.call(void 0, () => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
1502
+ const feedbackItems = _react.useMemo.call(void 0,
1503
+ () => _chunkY46ACXM4cjs.getOgTitleValidation.call(void 0, value || "", keywords, isParentseoField),
324
1504
  [value, keywords, isParentseoField]
325
1505
  );
326
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1506
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
327
1507
  renderDefault(props),
328
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
329
- /* @__PURE__ */ jsxRuntime.jsx(
1508
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1509
+ SeoGenButton,
1510
+ {
1511
+ field: "ogTitle",
1512
+ ai: options == null ? void 0 : options.ai,
1513
+ seoFieldPath: String(path[0]),
1514
+ onGenerate: (v) => onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, v)))
1515
+ }
1516
+ ),
1517
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1518
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
330
1519
  "div",
331
1520
  {
332
1521
  style: {
@@ -337,7 +1526,7 @@ var OgTitle = (props) => {
337
1526
  }
338
1527
  }
339
1528
  ),
340
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1529
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
341
1530
  ] }, item.text)) })
342
1531
  ] });
343
1532
  };
@@ -345,29 +1534,29 @@ var OgTitle_default = OgTitle;
345
1534
 
346
1535
  // src/schemas/types/openGraph/index.ts
347
1536
  function openGraph(config = {}) {
348
- return sanity.defineType({
1537
+ return _sanity.defineType.call(void 0, {
349
1538
  name: "openGraph",
350
1539
  title: "Open Graph Settings",
351
1540
  type: "object",
352
1541
  fields: [
353
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1542
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
354
1543
  name: "url",
355
1544
  type: "url"
356
1545
  }, getFieldInfo("openGraphUrl", config.fieldOverrides)), {
357
1546
  hidden: getFieldHiddenFunction("openGraphUrl", config),
358
1547
  description: "The canonical URL of the page. This should be the full URL including protocol (https://)."
359
1548
  })),
360
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1549
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
361
1550
  name: "title"
362
1551
  }, getFieldInfo("openGraphTitle", config.fieldOverrides)), {
363
1552
  type: "string",
364
1553
  hidden: getFieldHiddenFunction("openGraphTitle", config),
365
1554
  components: {
366
1555
  input: OgTitle_default
367
- // Can also wrap with a string input + preview
368
- }
1556
+ },
1557
+ options: _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {})
369
1558
  })),
370
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1559
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
371
1560
  name: "description"
372
1561
  }, getFieldInfo("openGraphDescription", config.fieldOverrides)), {
373
1562
  type: "text",
@@ -375,16 +1564,16 @@ function openGraph(config = {}) {
375
1564
  hidden: getFieldHiddenFunction("openGraphDescription", config),
376
1565
  components: {
377
1566
  input: OgDescription_default
378
- // Can also wrap with a text area + preview
379
- }
1567
+ },
1568
+ options: _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {})
380
1569
  })),
381
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1570
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
382
1571
  name: "siteName"
383
1572
  }, getFieldInfo("openGraphSiteName", config.fieldOverrides)), {
384
1573
  type: "string",
385
1574
  hidden: getFieldHiddenFunction("openGraphSiteName", config)
386
1575
  })),
387
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1576
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
388
1577
  name: "type"
389
1578
  }, getFieldInfo("openGraphType", config.fieldOverrides)), {
390
1579
  type: "string",
@@ -403,7 +1592,7 @@ function openGraph(config = {}) {
403
1592
  hidden: getFieldHiddenFunction("openGraphType", config),
404
1593
  initialValue: "website"
405
1594
  })),
406
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1595
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
407
1596
  name: "imageType"
408
1597
  }, getFieldInfo("openGraphImageType", config.fieldOverrides)), {
409
1598
  type: "string",
@@ -416,7 +1605,7 @@ function openGraph(config = {}) {
416
1605
  hidden: getFieldHiddenFunction("openGraphImage", config),
417
1606
  initialValue: "upload"
418
1607
  })),
419
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1608
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
420
1609
  name: "image"
421
1610
  }, getFieldInfo("openGraphImage", config.fieldOverrides)), {
422
1611
  type: "image",
@@ -427,7 +1616,7 @@ function openGraph(config = {}) {
427
1616
  input: OgImage_default
428
1617
  },
429
1618
  fields: [
430
- sanity.defineField({
1619
+ _sanity.defineField.call(void 0, {
431
1620
  name: "alt",
432
1621
  title: "Image Alt Text",
433
1622
  type: "string",
@@ -441,7 +1630,7 @@ function openGraph(config = {}) {
441
1630
  return typeof hiddenFn === "function" ? hiddenFn(context) : hiddenFn;
442
1631
  }
443
1632
  })),
444
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1633
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
445
1634
  name: "imageUrl"
446
1635
  }, getFieldInfo("openGraphImageUrl", config.fieldOverrides)), {
447
1636
  type: "url",
@@ -458,19 +1647,38 @@ function openGraph(config = {}) {
458
1647
  ]
459
1648
  });
460
1649
  }
1650
+
1651
+ // src/schemas/types/twitter/index.ts
1652
+
1653
+
1654
+ // src/components/twitter/twitterDescription.tsx
1655
+
1656
+
1657
+
1658
+
461
1659
  var TwitterDescription = (props) => {
462
- const { value, renderDefault, path } = props;
463
- const parent = sanity.useFormValue([path[0]]);
1660
+ const { value, renderDefault, path, schemaType, onChange } = props;
1661
+ const { options } = schemaType;
1662
+ const parent = _sanity.useFormValue.call(void 0, [path[0]]);
464
1663
  const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
465
- const keywords = react.useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
466
- const feedbackItems = react.useMemo(
467
- () => chunkZBHLMQTS_cjs.getTwitterDescriptionValidation(value || "", keywords, isParentseoField),
1664
+ const keywords = _react.useMemo.call(void 0, () => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
1665
+ const feedbackItems = _react.useMemo.call(void 0,
1666
+ () => _chunkY46ACXM4cjs.getTwitterDescriptionValidation.call(void 0, value || "", keywords, isParentseoField),
468
1667
  [value, keywords, isParentseoField]
469
1668
  );
470
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1669
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
471
1670
  renderDefault(props),
472
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
473
- /* @__PURE__ */ jsxRuntime.jsx(
1671
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1672
+ SeoGenButton,
1673
+ {
1674
+ field: "twitterDescription",
1675
+ ai: options == null ? void 0 : options.ai,
1676
+ seoFieldPath: String(path[0]),
1677
+ onGenerate: (v) => onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, v)))
1678
+ }
1679
+ ),
1680
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1681
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
474
1682
  "div",
475
1683
  {
476
1684
  style: {
@@ -481,25 +1689,31 @@ var TwitterDescription = (props) => {
481
1689
  }
482
1690
  }
483
1691
  ),
484
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1692
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
485
1693
  ] }, item.text)) })
486
1694
  ] });
487
1695
  };
488
1696
  var twitterDescription_default = TwitterDescription;
1697
+
1698
+ // src/components/twitter/TwitterImage.tsx
1699
+
1700
+
1701
+
1702
+
489
1703
  var TwitterImage = (props) => {
490
1704
  const { value, renderDefault, path } = props;
491
- const seoParent = sanity.useFormValue([path[0]]);
1705
+ const seoParent = _sanity.useFormValue.call(void 0, [path[0]]);
492
1706
  const imgValue = value;
493
1707
  const hasImage = !!(imgValue == null ? void 0 : imgValue.asset);
494
1708
  const altText = imgValue == null ? void 0 : imgValue.alt;
495
- const feedbackItems = react.useMemo(
496
- () => chunkZBHLMQTS_cjs.getTwitterImageValidation(hasImage, altText, seoParent),
1709
+ const feedbackItems = _react.useMemo.call(void 0,
1710
+ () => _chunkY46ACXM4cjs.getTwitterImageValidation.call(void 0, hasImage, altText, seoParent),
497
1711
  [hasImage, altText, seoParent]
498
1712
  );
499
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1713
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
500
1714
  renderDefault(props),
501
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
502
- /* @__PURE__ */ jsxRuntime.jsx(
1715
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1716
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
503
1717
  "div",
504
1718
  {
505
1719
  style: {
@@ -510,22 +1724,28 @@ var TwitterImage = (props) => {
510
1724
  }
511
1725
  }
512
1726
  ),
513
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1727
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
514
1728
  ] }, item.text)) })
515
1729
  ] });
516
1730
  };
517
1731
  var TwitterImage_default = TwitterImage;
1732
+
1733
+ // src/components/twitter/TwitterImageUrl.tsx
1734
+
1735
+
1736
+
1737
+
518
1738
  var TwitterImageUrl = (props) => {
519
1739
  const { value, renderDefault, path } = props;
520
- const seoParent = sanity.useFormValue([path[0]]);
521
- const feedbackItems = react.useMemo(
522
- () => chunkZBHLMQTS_cjs.getTwitterImageUrlValidation(value, seoParent),
1740
+ const seoParent = _sanity.useFormValue.call(void 0, [path[0]]);
1741
+ const feedbackItems = _react.useMemo.call(void 0,
1742
+ () => _chunkY46ACXM4cjs.getTwitterImageUrlValidation.call(void 0, value, seoParent),
523
1743
  [value, seoParent]
524
1744
  );
525
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1745
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
526
1746
  renderDefault(props),
527
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
528
- /* @__PURE__ */ jsxRuntime.jsx(
1747
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1748
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
529
1749
  "div",
530
1750
  {
531
1751
  style: {
@@ -536,24 +1756,40 @@ var TwitterImageUrl = (props) => {
536
1756
  }
537
1757
  }
538
1758
  ),
539
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1759
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
540
1760
  ] }, item.text)) })
541
1761
  ] });
542
1762
  };
543
1763
  var TwitterImageUrl_default = TwitterImageUrl;
1764
+
1765
+ // src/components/twitter/twitterTitle.tsx
1766
+
1767
+
1768
+
1769
+
544
1770
  var TwitterTitle = (props) => {
545
- const { value, renderDefault, path } = props;
546
- const parent = sanity.useFormValue([path[0]]);
1771
+ const { value, renderDefault, path, schemaType, onChange } = props;
1772
+ const { options } = schemaType;
1773
+ const parent = _sanity.useFormValue.call(void 0, [path[0]]);
547
1774
  const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
548
- const keywords = react.useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
549
- const feedbackItems = react.useMemo(
550
- () => chunkZBHLMQTS_cjs.getTwitterTitleValidation(value || "", keywords, isParentseoField),
1775
+ const keywords = _react.useMemo.call(void 0, () => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
1776
+ const feedbackItems = _react.useMemo.call(void 0,
1777
+ () => _chunkY46ACXM4cjs.getTwitterTitleValidation.call(void 0, value || "", keywords, isParentseoField),
551
1778
  [value, keywords, isParentseoField]
552
1779
  );
553
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
1780
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
554
1781
  renderDefault(props),
555
- /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
556
- /* @__PURE__ */ jsxRuntime.jsx(
1782
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1783
+ SeoGenButton,
1784
+ {
1785
+ field: "twitterTitle",
1786
+ ai: options == null ? void 0 : options.ai,
1787
+ seoFieldPath: String(path[0]),
1788
+ onGenerate: (v) => onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, v)))
1789
+ }
1790
+ ),
1791
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
1792
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
557
1793
  "div",
558
1794
  {
559
1795
  style: {
@@ -564,7 +1800,7 @@ var TwitterTitle = (props) => {
564
1800
  }
565
1801
  }
566
1802
  ),
567
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
1803
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
568
1804
  ] }, item.text)) })
569
1805
  ] });
570
1806
  };
@@ -572,12 +1808,12 @@ var twitterTitle_default = TwitterTitle;
572
1808
 
573
1809
  // src/schemas/types/twitter/index.ts
574
1810
  function twitter(config = {}) {
575
- return sanity.defineType({
1811
+ return _sanity.defineType.call(void 0, {
576
1812
  name: "twitter",
577
1813
  title: "X (Formerly Twitter)",
578
1814
  type: "object",
579
1815
  fields: [
580
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1816
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
581
1817
  name: "card"
582
1818
  }, getFieldInfo("twitterCard", config.fieldOverrides)), {
583
1819
  type: "string",
@@ -593,28 +1829,29 @@ function twitter(config = {}) {
593
1829
  initialValue: "summary_large_image"
594
1830
  // good default
595
1831
  })),
596
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1832
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
597
1833
  name: "site"
598
1834
  }, getFieldInfo("twitterSite", config.fieldOverrides)), {
599
1835
  type: "string",
600
1836
  hidden: getFieldHiddenFunction("twitterSite", config)
601
1837
  })),
602
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1838
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
603
1839
  name: "creator",
604
1840
  type: "string"
605
1841
  }, getFieldInfo("twitterCreator", config.fieldOverrides)), {
606
1842
  hidden: getFieldHiddenFunction("twitterCreator", config)
607
1843
  })),
608
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1844
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
609
1845
  name: "title",
610
1846
  type: "string"
611
1847
  }, getFieldInfo("twitterTitle", config.fieldOverrides)), {
612
1848
  hidden: getFieldHiddenFunction("twitterTitle", config),
613
1849
  components: {
614
1850
  input: twitterTitle_default
615
- }
1851
+ },
1852
+ options: _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {})
616
1853
  })),
617
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1854
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
618
1855
  name: "description",
619
1856
  type: "text",
620
1857
  rows: 3
@@ -622,9 +1859,10 @@ function twitter(config = {}) {
622
1859
  hidden: getFieldHiddenFunction("twitterDescription", config),
623
1860
  components: {
624
1861
  input: twitterDescription_default
625
- }
1862
+ },
1863
+ options: _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {})
626
1864
  })),
627
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1865
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
628
1866
  name: "imageType"
629
1867
  }, getFieldInfo("twitterImageType", config.fieldOverrides)), {
630
1868
  type: "string",
@@ -637,7 +1875,7 @@ function twitter(config = {}) {
637
1875
  hidden: getFieldHiddenFunction("twitterImage", config),
638
1876
  initialValue: "upload"
639
1877
  })),
640
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1878
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
641
1879
  name: "image"
642
1880
  }, getFieldInfo("twitterImage", config.fieldOverrides)), {
643
1881
  type: "image",
@@ -648,7 +1886,7 @@ function twitter(config = {}) {
648
1886
  input: TwitterImage_default
649
1887
  },
650
1888
  fields: [
651
- sanity.defineField({
1889
+ _sanity.defineField.call(void 0, {
652
1890
  name: "alt",
653
1891
  title: "Image Alt Text",
654
1892
  type: "string",
@@ -662,7 +1900,7 @@ function twitter(config = {}) {
662
1900
  return typeof hiddenFn === "function" ? hiddenFn(context) : hiddenFn;
663
1901
  }
664
1902
  })),
665
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
1903
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
666
1904
  name: "imageUrl"
667
1905
  }, getFieldInfo("twitterImageUrl", config.fieldOverrides)), {
668
1906
  type: "url",
@@ -681,8 +1919,8 @@ function twitter(config = {}) {
681
1919
  }
682
1920
 
683
1921
  // src/schemas/index.ts
684
- var LazySeoPreview = react.lazy(() => import('./SeoPreview-WYH7NYNM.cjs'));
685
- var SeoPreviewWrapper = (props) => react.createElement(react.Suspense, { fallback: null }, react.createElement(LazySeoPreview, props));
1922
+ var LazySeoPreview = _react.lazy.call(void 0, () => Promise.resolve().then(() => _interopRequireWildcard(require("./SeoPreview-LMZAWWOE.cjs"))));
1923
+ var SeoPreviewWrapper = (props) => _react.createElement.call(void 0, _react.Suspense, { fallback: null }, _react.createElement.call(void 0, LazySeoPreview, props));
686
1924
  function buildFieldGroupMap(groups) {
687
1925
  const map = /* @__PURE__ */ new Map();
688
1926
  for (const g of groups) {
@@ -714,20 +1952,88 @@ function withGroup(field, fieldGroupMap) {
714
1952
  if (!fieldGroupMap) return field;
715
1953
  const groups = fieldGroupMap.get(field.name);
716
1954
  if (!groups || groups.length === 0) return field;
717
- return chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({}, field), { group: groups.length === 1 ? groups[0] : groups });
1955
+ return _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, field), { group: groups.length === 1 ? groups[0] : groups });
1956
+ }
1957
+ function shouldShowPreviewField(seoPreview) {
1958
+ if (typeof seoPreview === "boolean") return seoPreview;
1959
+ if (typeof seoPreview === "object") return !isEmpty(seoPreview);
1960
+ return false;
1961
+ }
1962
+ function getSeoPreviewSuffixOptions(seoPreview) {
1963
+ if (typeof seoPreview !== "object" || !seoPreview) return {};
1964
+ const extra = {};
1965
+ if (seoPreview.titleSuffix) extra.titleSuffix = seoPreview.titleSuffix;
1966
+ if (seoPreview.titleSuffixQuery) extra.titleSuffixQuery = seoPreview.titleSuffixQuery;
1967
+ return extra;
1968
+ }
1969
+ function getPreviewFieldOptions(config) {
1970
+ const seoPreview = config.seoPreview;
1971
+ const extra = {};
1972
+ if (typeof seoPreview === "object" && seoPreview) {
1973
+ if (seoPreview.prefix) extra.prefix = seoPreview.prefix;
1974
+ if (seoPreview.titleSuffixInheritColor) {
1975
+ extra.titleSuffixInheritColor = seoPreview.titleSuffixInheritColor;
1976
+ }
1977
+ }
1978
+ return _chunkA57XNQC7cjs.__spreadValues.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
1979
+ baseUrl: config.baseUrl || "https://www.example.com"
1980
+ }, config.apiVersion ? { apiVersion: config.apiVersion } : {}), getSeoPreviewSuffixOptions(seoPreview)), extra);
1981
+ }
1982
+ function getAiOption(config) {
1983
+ return config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {};
1984
+ }
1985
+ function getTitleFieldOptions(config) {
1986
+ return _chunkA57XNQC7cjs.__spreadValues.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, config.apiVersion ? { apiVersion: config.apiVersion } : {}), getSeoPreviewSuffixOptions(config.seoPreview)), getAiOption(config));
1987
+ }
1988
+ function buildGeoChecklistField(config, fieldGroupMap) {
1989
+ if (config.geo === false) return [];
1990
+ return [
1991
+ withGroup(
1992
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
1993
+ name: "geoChecklist"
1994
+ }, getFieldInfo("geoChecklist", config.fieldOverrides)), {
1995
+ type: "string",
1996
+ readOnly: true,
1997
+ components: {
1998
+ input: GEOChecklist_default
1999
+ },
2000
+ options: _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, config.licenseKey ? { licenseKey: config.licenseKey } : {}),
2001
+ hidden: getFieldHiddenFunction("geoChecklist", config)
2002
+ })),
2003
+ fieldGroupMap
2004
+ )
2005
+ ];
2006
+ }
2007
+ function buildMetaTagsPreviewField(config, fieldGroupMap) {
2008
+ if (config.metaTagsPreview === false) return [];
2009
+ return [
2010
+ withGroup(
2011
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
2012
+ name: "metaTagsPreview"
2013
+ }, getFieldInfo("metaTagsPreview", config.fieldOverrides)), {
2014
+ type: "string",
2015
+ readOnly: true,
2016
+ components: {
2017
+ input: MetaTagsPreview_default
2018
+ },
2019
+ hidden: getFieldHiddenFunction("metaTagsPreview", config)
2020
+ })),
2021
+ fieldGroupMap
2022
+ )
2023
+ ];
718
2024
  }
719
2025
  function seoFieldsSchema(config = {}) {
720
2026
  const groupsCfg = config.fieldGroups;
721
2027
  const fieldGroupMap = (groupsCfg == null ? void 0 : groupsCfg.length) ? buildFieldGroupMap(groupsCfg) : void 0;
722
2028
  const sanityGroups = (groupsCfg == null ? void 0 : groupsCfg.length) ? toSanityGroups(groupsCfg) : void 0;
723
- return sanity.defineType(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2029
+ return _sanity.defineType.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
724
2030
  name: "seoFields",
725
2031
  title: "SEO Fields",
726
2032
  type: "object"
727
2033
  }, sanityGroups ? { groups: sanityGroups } : {}), {
728
2034
  fields: [
729
2035
  withGroup(
730
- sanity.defineField({
2036
+ _sanity.defineField.call(void 0, {
731
2037
  name: "robots",
732
2038
  title: "Robots Settings",
733
2039
  type: "robots",
@@ -736,16 +2042,14 @@ function seoFieldsSchema(config = {}) {
736
2042
  fieldGroupMap
737
2043
  ),
738
2044
  // 👇 conditionally spread preview field
739
- ...typeof config.seoPreview === "boolean" && config.seoPreview || typeof config.seoPreview === "object" && !isEmpty(config.seoPreview) ? [
2045
+ ...shouldShowPreviewField(config.seoPreview) ? [
740
2046
  withGroup(
741
- sanity.defineField({
2047
+ _sanity.defineField.call(void 0, {
742
2048
  name: "preview",
743
2049
  title: "SEO Preview",
744
2050
  type: "string",
745
2051
  components: { input: SeoPreviewWrapper },
746
- options: chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues(chunkS367Y35J_cjs.__spreadValues({
747
- baseUrl: config.baseUrl || "https://www.example.com"
748
- }, 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 } : {}),
2052
+ options: getPreviewFieldOptions(config),
749
2053
  initialValue: "",
750
2054
  readOnly: true
751
2055
  }),
@@ -753,20 +2057,20 @@ function seoFieldsSchema(config = {}) {
753
2057
  )
754
2058
  ] : [],
755
2059
  withGroup(
756
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2060
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
757
2061
  name: "title"
758
2062
  }, getFieldInfo("title", config.fieldOverrides)), {
759
2063
  type: "string",
760
2064
  components: {
761
2065
  input: MetaTitle_default
762
2066
  },
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 } : {}),
2067
+ options: getTitleFieldOptions(config),
764
2068
  hidden: getFieldHiddenFunction("title", config)
765
2069
  })),
766
2070
  fieldGroupMap
767
2071
  ),
768
2072
  withGroup(
769
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2073
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
770
2074
  name: "description"
771
2075
  }, getFieldInfo("description", config.fieldOverrides)), {
772
2076
  type: "text",
@@ -774,12 +2078,13 @@ function seoFieldsSchema(config = {}) {
774
2078
  components: {
775
2079
  input: MetaDescription_default
776
2080
  },
2081
+ options: getAiOption(config),
777
2082
  hidden: getFieldHiddenFunction("description", config)
778
2083
  })),
779
2084
  fieldGroupMap
780
2085
  ),
781
2086
  withGroup(
782
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2087
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
783
2088
  name: "metaImage"
784
2089
  }, getFieldInfo("metaImage", config.fieldOverrides)), {
785
2090
  type: "image",
@@ -794,7 +2099,7 @@ function seoFieldsSchema(config = {}) {
794
2099
  fieldGroupMap
795
2100
  ),
796
2101
  withGroup(
797
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2102
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
798
2103
  name: "metaAttributes"
799
2104
  }, getFieldInfo("metaAttributes", config.fieldOverrides)), {
800
2105
  type: "array",
@@ -804,19 +2109,21 @@ function seoFieldsSchema(config = {}) {
804
2109
  fieldGroupMap
805
2110
  ),
806
2111
  withGroup(
807
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2112
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
808
2113
  name: "keywords"
809
2114
  }, getFieldInfo("keywords", config.fieldOverrides)), {
810
2115
  title: "Keywords",
811
2116
  type: "array",
812
2117
  of: [{ type: "string" }],
813
2118
  description: "Add relevant keywords for this page. These keywords will be used for SEO purposes.",
2119
+ components: config.ai ? { input: KeywordsInput_default } : void 0,
2120
+ options: getAiOption(config),
814
2121
  hidden: getFieldHiddenFunction("keywords", config)
815
2122
  })),
816
2123
  fieldGroupMap
817
2124
  ),
818
2125
  withGroup(
819
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2126
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
820
2127
  name: "canonicalUrl"
821
2128
  }, getFieldInfo("canonicalUrl", config.fieldOverrides)), {
822
2129
  title: "Canonical URL",
@@ -826,18 +2133,47 @@ function seoFieldsSchema(config = {}) {
826
2133
  })),
827
2134
  fieldGroupMap
828
2135
  ),
2136
+ withGroup(
2137
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
2138
+ name: "focusKeyword"
2139
+ }, getFieldInfo("focusKeyword", config.fieldOverrides)), {
2140
+ type: "string",
2141
+ components: {
2142
+ input: FocusKeywordInput_default
2143
+ },
2144
+ options: getAiOption(config),
2145
+ hidden: getFieldHiddenFunction("focusKeyword", config)
2146
+ })),
2147
+ fieldGroupMap
2148
+ ),
2149
+ withGroup(
2150
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
2151
+ name: "hreflangs"
2152
+ }, getFieldInfo("hreflangs", config.fieldOverrides)), {
2153
+ type: "array",
2154
+ of: [{ type: "hreflangEntry" }],
2155
+ description: "Add alternate language/region versions. Include x-default for the fallback URL.",
2156
+ hidden: getFieldHiddenFunction("hreflangs", config)
2157
+ })),
2158
+ fieldGroupMap
2159
+ ),
2160
+ ...buildGeoChecklistField(config, fieldGroupMap),
2161
+ ...buildMetaTagsPreviewField(config, fieldGroupMap),
829
2162
  withGroup(openGraph(config), fieldGroupMap),
830
2163
  withGroup(twitter(config), fieldGroupMap)
831
2164
  ]
832
2165
  }));
833
2166
  }
2167
+
2168
+ // src/schemas/types/baseMeta/index.ts
2169
+
834
2170
  function baseMeta(config = {}) {
835
- return sanity.defineType({
2171
+ return _sanity.defineType.call(void 0, {
836
2172
  name: "baseMeta",
837
2173
  title: "Basic Meta Settings",
838
2174
  type: "object",
839
2175
  fields: [
840
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2176
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
841
2177
  name: "title"
842
2178
  }, getFieldInfo("title", config.fieldOverrides)), {
843
2179
  type: "string",
@@ -846,7 +2182,7 @@ function baseMeta(config = {}) {
846
2182
  },
847
2183
  hidden: getFieldHiddenFunction("title", config)
848
2184
  })),
849
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2185
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
850
2186
  name: "description"
851
2187
  }, getFieldInfo("description", config.fieldOverrides)), {
852
2188
  type: "text",
@@ -856,7 +2192,7 @@ function baseMeta(config = {}) {
856
2192
  },
857
2193
  hidden: getFieldHiddenFunction("description", config)
858
2194
  })),
859
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2195
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
860
2196
  name: "metaImage"
861
2197
  }, getFieldInfo("metaImage", config.fieldOverrides)), {
862
2198
  type: "image",
@@ -868,7 +2204,7 @@ function baseMeta(config = {}) {
868
2204
  },
869
2205
  hidden: getFieldHiddenFunction("metaImage", config)
870
2206
  })),
871
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2207
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
872
2208
  name: "keywords"
873
2209
  }, getFieldInfo("keywords", config.fieldOverrides)), {
874
2210
  title: "Keywords",
@@ -877,7 +2213,7 @@ function baseMeta(config = {}) {
877
2213
  description: "Add relevant keywords for this page. These keywords will be used for SEO purposes.",
878
2214
  hidden: getFieldHiddenFunction("keywords", config)
879
2215
  })),
880
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2216
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
881
2217
  name: "canonicalUrl"
882
2218
  }, getFieldInfo("canonicalUrl", config.fieldOverrides)), {
883
2219
  title: "Canonical URL",
@@ -885,7 +2221,7 @@ function baseMeta(config = {}) {
885
2221
  description: "Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.",
886
2222
  hidden: getFieldHiddenFunction("canonicalUrl", config)
887
2223
  })),
888
- sanity.defineField(chunkS367Y35J_cjs.__spreadProps(chunkS367Y35J_cjs.__spreadValues({
2224
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
889
2225
  name: "metaAttributes"
890
2226
  }, getFieldInfo("metaAttributes", config.fieldOverrides)), {
891
2227
  type: "array",
@@ -895,17 +2231,55 @@ function baseMeta(config = {}) {
895
2231
  ]
896
2232
  });
897
2233
  }
898
- var metaAttribute_default = sanity.defineType({
2234
+
2235
+ // src/schemas/types/hreflang/index.ts
2236
+
2237
+ var hreflang_default = _sanity.defineType.call(void 0, {
2238
+ name: "hreflangEntry",
2239
+ title: "Hreflang Entry",
2240
+ type: "object",
2241
+ fields: [
2242
+ _sanity.defineField.call(void 0, {
2243
+ name: "locale",
2244
+ title: "Language / Region",
2245
+ type: "string",
2246
+ description: "BCP 47 tag \u2014 e.g. en, fr-FR, x-default",
2247
+ validation: (Rule) => Rule.required().custom((value) => {
2248
+ if (!value) return "Required";
2249
+ if (!/^(x-default|[a-z]{2,3}(-[A-Z]{2,3})?)$/.test(value)) {
2250
+ return "Must be a valid BCP 47 tag (e.g. en, fr-FR, x-default)";
2251
+ }
2252
+ return true;
2253
+ })
2254
+ }),
2255
+ _sanity.defineField.call(void 0, {
2256
+ name: "url",
2257
+ title: "URL",
2258
+ type: "url",
2259
+ validation: (Rule) => Rule.required().uri({ scheme: ["http", "https"] })
2260
+ })
2261
+ ],
2262
+ preview: {
2263
+ select: {
2264
+ title: "locale",
2265
+ subtitle: "url"
2266
+ }
2267
+ }
2268
+ });
2269
+
2270
+ // src/schemas/types/metaAttribute/index.ts
2271
+
2272
+ var metaAttribute_default = _sanity.defineType.call(void 0, {
899
2273
  name: "metaAttribute",
900
2274
  title: "Meta Attribute",
901
2275
  type: "object",
902
2276
  fields: [
903
- sanity.defineField({
2277
+ _sanity.defineField.call(void 0, {
904
2278
  name: "key",
905
2279
  title: "Attribute Name",
906
2280
  type: "string"
907
2281
  }),
908
- sanity.defineField({
2282
+ _sanity.defineField.call(void 0, {
909
2283
  name: "type",
910
2284
  title: "Attribute Value Type",
911
2285
  type: "string",
@@ -917,13 +2291,13 @@ var metaAttribute_default = sanity.defineType({
917
2291
  },
918
2292
  initialValue: "string"
919
2293
  }),
920
- sanity.defineField({
2294
+ _sanity.defineField.call(void 0, {
921
2295
  name: "value",
922
2296
  title: "Attribute Value",
923
2297
  type: "string",
924
2298
  hidden: ({ parent }) => (parent == null ? void 0 : parent.type) === "image"
925
2299
  }),
926
- sanity.defineField({
2300
+ _sanity.defineField.call(void 0, {
927
2301
  name: "image",
928
2302
  title: "Attribute Image Value",
929
2303
  type: "image",
@@ -953,7 +2327,10 @@ var metaAttribute_default = sanity.defineType({
953
2327
  }
954
2328
  }
955
2329
  });
956
- var metaTag_default = sanity.defineType({
2330
+
2331
+ // src/schemas/types/metaTag/index.ts
2332
+
2333
+ var metaTag_default = _sanity.defineType.call(void 0, {
957
2334
  name: "metaTag",
958
2335
  title: "Meta Tag",
959
2336
  type: "object",
@@ -967,27 +2344,44 @@ var metaTag_default = sanity.defineType({
967
2344
  }
968
2345
  ]
969
2346
  });
970
- var robots_default = sanity.defineType({
2347
+
2348
+ // src/schemas/types/robots/index.ts
2349
+
2350
+ var robots_default = _sanity.defineType.call(void 0, {
971
2351
  name: "robots",
972
2352
  title: "Robots Settings",
973
2353
  type: "object",
974
2354
  fields: [
975
- sanity.defineField({
2355
+ _sanity.defineField.call(void 0, {
976
2356
  name: "noIndex",
977
2357
  title: "No Index",
978
2358
  type: "boolean",
979
2359
  initialValue: false,
980
2360
  description: "Enable this to prevent search engines from indexing this page. The page will not appear in search results."
981
2361
  }),
982
- sanity.defineField({
2362
+ _sanity.defineField.call(void 0, {
983
2363
  name: "noFollow",
984
2364
  title: "No Follow",
985
2365
  type: "boolean",
986
2366
  initialValue: false,
987
2367
  description: "Enable this to prevent search engines from following links on this page. Links will not pass SEO value."
2368
+ }),
2369
+ _sanity.defineField.call(void 0, {
2370
+ name: "noTranslate",
2371
+ title: "No Translate",
2372
+ type: "boolean",
2373
+ initialValue: false,
2374
+ description: "Enable this to prevent search engines from offering a translated version of this page in search results."
2375
+ }),
2376
+ _sanity.defineField.call(void 0, {
2377
+ name: "noImageIndex",
2378
+ title: "No Image Index",
2379
+ type: "boolean",
2380
+ initialValue: false,
2381
+ description: "Enable this to prevent images on this page from appearing in image search results."
988
2382
  })
989
2383
  ],
990
- description: "Select how search engines should index and follow links on this page."
2384
+ description: "Select how search engines should index, follow links, translate content, and index images on this page."
991
2385
  });
992
2386
 
993
2387
  // src/schemas/types/index.ts
@@ -1003,13 +2397,14 @@ function types(config = {}) {
1003
2397
  // pass config here
1004
2398
  metaAttribute_default,
1005
2399
  metaTag_default,
1006
- robots_default
2400
+ robots_default,
2401
+ hreflang_default
1007
2402
  ];
1008
2403
  }
1009
2404
 
1010
2405
  // 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;
2406
+ var resolveDashboardConfig = (healthDashboard, rootApiVersion, rootLicenseKey) => {
2407
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
1013
2408
  const cfg = typeof healthDashboard === "object" ? healthDashboard : void 0;
1014
2409
  const deprecationWarnings = [];
1015
2410
  if ((cfg == null ? void 0 : cfg.apiVersion) !== void 0) {
@@ -1022,27 +2417,37 @@ var resolveDashboardConfig = (healthDashboard, rootApiVersion) => {
1022
2417
  '[sanity-plugin-seofields] "healthDashboard.apiVersion" is deprecated. Move it to the root "apiVersion" option instead.'
1023
2418
  );
1024
2419
  }
2420
+ if ((cfg == null ? void 0 : cfg.licenseKey) !== void 0 && !rootLicenseKey) {
2421
+ deprecationWarnings.push({
2422
+ key: "healthDashboard.licenseKey \u2192 licenseKey",
2423
+ version: "v1.8.0",
2424
+ changelogUrl: "https://github.com/hardik-143/sanity-plugin-seofields/blob/main/CHANGELOG.md"
2425
+ });
2426
+ console.warn(
2427
+ '[sanity-plugin-seofields] "healthDashboard.licenseKey" is deprecated. Move it to the root "licenseKey" option instead.'
2428
+ );
2429
+ }
1025
2430
  return {
1026
2431
  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,
2432
+ 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",
2433
+ toolName: (_e = (_d = cfg == null ? void 0 : cfg.tool) == null ? void 0 : _d.name) != null ? _e : "seo-health-dashboard",
2434
+ icon: (_f = cfg == null ? void 0 : cfg.content) == null ? void 0 : _f.icon,
2435
+ title: (_g = cfg == null ? void 0 : cfg.content) == null ? void 0 : _g.title,
2436
+ description: (_h = cfg == null ? void 0 : cfg.content) == null ? void 0 : _h.description,
1032
2437
  showTypeColumn: cfg == null ? void 0 : cfg.showTypeColumn,
1033
2438
  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,
2439
+ queryTypes: (_i = cfg == null ? void 0 : cfg.query) == null ? void 0 : _i.types,
2440
+ queryRequireSeo: (_j = cfg == null ? void 0 : cfg.query) == null ? void 0 : _j.requireSeo,
2441
+ queryGroq: (_k = cfg == null ? void 0 : cfg.query) == null ? void 0 : _k.groq,
2442
+ apiVersion: (_l = cfg == null ? void 0 : cfg.apiVersion) != null ? _l : rootApiVersion,
2443
+ licenseKey: rootLicenseKey != null ? rootLicenseKey : cfg == null ? void 0 : cfg.licenseKey,
1039
2444
  typeDisplayLabels: cfg == null ? void 0 : cfg.typeDisplayLabels,
1040
2445
  typeColumnMode: cfg == null ? void 0 : cfg.typeColumnMode,
1041
2446
  titleField: cfg == null ? void 0 : cfg.titleField,
1042
2447
  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,
2448
+ loadingLicense: (_m = cfg == null ? void 0 : cfg.content) == null ? void 0 : _m.loadingLicense,
2449
+ loadingDocuments: (_n = cfg == null ? void 0 : cfg.content) == null ? void 0 : _n.loadingDocuments,
2450
+ noDocuments: (_o = cfg == null ? void 0 : cfg.content) == null ? void 0 : _o.noDocuments,
1046
2451
  previewMode: cfg == null ? void 0 : cfg.previewMode,
1047
2452
  structureTool: cfg == null ? void 0 : cfg.structureTool,
1048
2453
  exportEnabled: (() => {
@@ -1057,18 +2462,19 @@ var resolveDashboardConfig = (healthDashboard, rootApiVersion) => {
1057
2462
  if (typeof exportCfg === "object" && exportCfg.formats) return exportCfg.formats;
1058
2463
  return ["csv", "json"];
1059
2464
  })(),
1060
- compactStats: (_o = cfg == null ? void 0 : cfg.compactStats) != null ? _o : false,
2465
+ compactStats: (_p = cfg == null ? void 0 : cfg.compactStats) != null ? _p : false,
1061
2466
  deprecationWarnings
1062
2467
  };
1063
2468
  };
1064
- var seofields = sanity.definePlugin((config = {}) => {
1065
- const { healthDashboard = true } = config;
1066
- const dash = resolveDashboardConfig(healthDashboard, config.apiVersion);
1067
- const LazySeoHealthTool = react.lazy(() => import('./SeoHealthTool-CZZRMWQK.cjs'));
1068
- const BoundSeoHealthTool = () => react.createElement(
1069
- react.Suspense,
2469
+ var seofields = _sanity.definePlugin.call(void 0, (config = {}) => {
2470
+ const cfg = config;
2471
+ const { healthDashboard = true, licenseKey, publishGate } = cfg;
2472
+ const dash = resolveDashboardConfig(healthDashboard, cfg.apiVersion, licenseKey);
2473
+ const LazySeoHealthTool = _react.lazy.call(void 0, () => Promise.resolve().then(() => _interopRequireWildcard(require("./SeoHealthTool-2COI27KI.cjs"))));
2474
+ const BoundSeoHealthTool = () => _react.createElement.call(void 0,
2475
+ _react.Suspense,
1070
2476
  { fallback: null },
1071
- react.createElement(LazySeoHealthTool, {
2477
+ _react.createElement.call(void 0, LazySeoHealthTool, {
1072
2478
  icon: dash.icon,
1073
2479
  title: dash.title,
1074
2480
  description: dash.description,
@@ -1094,12 +2500,23 @@ var seofields = sanity.definePlugin((config = {}) => {
1094
2500
  _deprecationWarnings: dash.deprecationWarnings
1095
2501
  })
1096
2502
  );
1097
- return chunkS367Y35J_cjs.__spreadValues({
2503
+ return _chunkA57XNQC7cjs.__spreadValues.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
1098
2504
  name: "sanity-plugin-seofields",
1099
2505
  schema: {
1100
- types: types(config)
2506
+ types: types(cfg)
2507
+ }
2508
+ }, publishGate && licenseKey && {
2509
+ document: {
2510
+ actions: (prev, context) => {
2511
+ var _a;
2512
+ if (((_a = publishGate.types) == null ? void 0 : _a.length) && !publishGate.types.includes(context.schemaType))
2513
+ return prev;
2514
+ return prev.map(
2515
+ (Action) => Action.action === "publish" ? _seofieldspro.createSeoPublishGateAction.call(void 0, Action, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, publishGate), { licenseKey })) : Action
2516
+ );
2517
+ }
1101
2518
  }
1102
- }, dash.enabled && {
2519
+ }), dash.enabled && {
1103
2520
  tools: [
1104
2521
  {
1105
2522
  name: dash.toolName,
@@ -1111,37 +2528,187 @@ var seofields = sanity.definePlugin((config = {}) => {
1111
2528
  });
1112
2529
  });
1113
2530
  var plugin_default = seofields;
1114
- var LazySeoHealthDashboard = react.lazy(() => import('./SeoHealthDashboard-IP5BJ322.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
- }
2531
+
2532
+ // src/components/SeoHealthPane.tsx
2533
+
2534
+
2535
+
2536
+ // src/utils/scoring.ts
2537
+ var getStatusCategory = (score) => {
2538
+ if (score >= 80) return "excellent";
2539
+ if (score >= 60) return "good";
2540
+ if (score >= 40) return "fair";
2541
+ if (score > 0) return "poor";
2542
+ return "missing";
2543
+ };
2544
+ var scoreMetaTitle = (title) => {
2545
+ const issues = [];
2546
+ let score = 0;
2547
+ if (title && title.length >= 50 && title.length <= 60) {
2548
+ score = 15;
2549
+ } else if (title && title.length > 0) {
2550
+ score = 10;
2551
+ if (title.length < 50) issues.push("Meta title too short (< 50 chars)");
2552
+ if (title.length > 60) issues.push("Meta title too long (> 60 chars)");
2553
+ } else {
2554
+ issues.push("Missing meta title");
2555
+ }
2556
+ return { score, issues };
2557
+ };
2558
+ var scoreMetaDescription = (description) => {
2559
+ const issues = [];
2560
+ let score = 0;
2561
+ if (description && description.length >= 120 && description.length <= 160) {
2562
+ score = 15;
2563
+ } else if (description && description.length > 0) {
2564
+ score = 10;
2565
+ if (description.length < 120) issues.push("Meta description too short (< 120 chars)");
2566
+ if (description.length > 160) issues.push("Meta description too long (> 160 chars)");
2567
+ } else {
2568
+ issues.push("Missing meta description");
2569
+ }
2570
+ return { score, issues };
2571
+ };
2572
+ var scoreOpenGraph = (openGraph2) => {
2573
+ const issues = [];
2574
+ let score = 0;
2575
+ if (openGraph2) {
2576
+ if (openGraph2.title) score += 6;
2577
+ else issues.push("Missing OG title");
2578
+ if (openGraph2.description) score += 6;
2579
+ else issues.push("Missing OG description");
2580
+ if (openGraph2.image) score += 6;
2581
+ else issues.push("Missing OG image");
2582
+ if (openGraph2.type) score += 7;
2583
+ else issues.push("Missing OG type");
2584
+ } else {
2585
+ issues.push("Open Graph not configured");
2586
+ }
2587
+ return { score, issues };
2588
+ };
2589
+ var scoreTwitterCard = (twitter2) => {
2590
+ const issues = [];
2591
+ let score = 0;
2592
+ if (twitter2) {
2593
+ if (twitter2.title) score += 5;
2594
+ else issues.push("Missing Twitter title");
2595
+ if (twitter2.description) score += 5;
2596
+ else issues.push("Missing Twitter description");
2597
+ if (twitter2.image) score += 5;
2598
+ else issues.push("Missing Twitter image");
2599
+ } else {
2600
+ issues.push("Twitter Card not configured");
2601
+ }
2602
+ return { score, issues };
2603
+ };
2604
+ var scoreKeywordArray = (keywords, title, description) => {
2605
+ if (!keywords || keywords.length === 0) {
2606
+ return { score: 0, issues: ["No keywords defined"], stuffed: false };
2607
+ }
2608
+ const issues = [];
2609
+ let score = 2;
2610
+ if (_chunkY46ACXM4cjs.hasMatchingKeyword.call(void 0, title || "", keywords) || _chunkY46ACXM4cjs.hasMatchingKeyword.call(void 0, description || "", keywords)) {
2611
+ score += 3;
2612
+ } else {
2613
+ issues.push("Keywords defined but not used in title or description");
2614
+ }
2615
+ const stuffed = _chunkY46ACXM4cjs.hasKeywordOveruse.call(void 0, title || "", keywords) || _chunkY46ACXM4cjs.hasKeywordOveruse.call(void 0, description || "", keywords);
2616
+ if (stuffed) issues.push("Keyword stuffing detected \u2014 reduce repeated keyword usage");
2617
+ return { score, issues, stuffed };
2618
+ };
2619
+ var scoreFocusKeyword = (focusKeyword, title, description) => {
2620
+ const placement = _chunkY46ACXM4cjs.getFocusKeywordPlacement.call(void 0, focusKeyword, title, description);
2621
+ if (!placement.hasFocusKeyword) {
2622
+ return { score: 0, issues: ["No focus keyword defined"], stuffed: false };
2623
+ }
2624
+ const issues = [];
2625
+ let score = 1;
2626
+ if (placement.atStartOfTitle) {
2627
+ score += 4;
2628
+ } else if (placement.inTitle) {
2629
+ score += 2;
2630
+ issues.push("Focus keyword is in the title but not at the start");
2631
+ } else if (placement.inDescription) {
2632
+ score += 1;
2633
+ issues.push("Focus keyword missing from meta title");
2634
+ } else {
2635
+ issues.push("Focus keyword missing from meta title and description");
2636
+ }
2637
+ if (placement.isStuffed)
2638
+ issues.push("Focus keyword appears too many times in title \u2014 avoid stuffing");
2639
+ return { score, issues, stuffed: placement.isStuffed };
2640
+ };
2641
+ var scoreKeywordOptimization = (keywords, focusKeyword, title, description) => {
2642
+ const arrayResult = scoreKeywordArray(keywords, title, description);
2643
+ const focusResult = scoreFocusKeyword(focusKeyword, title, description);
2644
+ let score = arrayResult.score + focusResult.score;
2645
+ if (arrayResult.stuffed || focusResult.stuffed) score = Math.max(0, score - 2);
2646
+ return { score: Math.min(score, 10), issues: [...arrayResult.issues, ...focusResult.issues] };
2647
+ };
2648
+ var calculateHealthScore = (doc) => {
2649
+ if (!doc.seo) {
2650
+ return { score: 0, status: "missing", issues: ["SEO fields not configured"] };
2651
+ }
2652
+ const seo = doc.seo;
2653
+ const title = seo.title;
2654
+ const description = seo.description;
2655
+ const keywords = seo.keywords;
2656
+ const focusKeyword = seo.focusKeyword;
2657
+ const robots = seo.robots;
2658
+ const openGraph2 = seo.openGraph;
2659
+ const twitter2 = seo.twitter;
2660
+ let score = 0;
2661
+ const issues = [];
2662
+ const titleResult = scoreMetaTitle(title);
2663
+ score += titleResult.score;
2664
+ issues.push(...titleResult.issues);
2665
+ const descResult = scoreMetaDescription(description);
2666
+ score += descResult.score;
2667
+ issues.push(...descResult.issues);
2668
+ if (seo.metaImage) score += 10;
2669
+ else issues.push("Missing meta image");
2670
+ const keywordResult = scoreKeywordOptimization(keywords, focusKeyword, title, description);
2671
+ score += keywordResult.score;
2672
+ issues.push(...keywordResult.issues);
2673
+ if (robots && !robots.noIndex) score += 5;
2674
+ else if (!robots) score += 5;
2675
+ const ogResult = scoreOpenGraph(openGraph2);
2676
+ score += ogResult.score;
2677
+ issues.push(...ogResult.issues);
2678
+ const twResult = scoreTwitterCard(twitter2);
2679
+ score += twResult.score;
2680
+ issues.push(...twResult.issues);
2681
+ const hasMetaImage = !!seo.metaImage;
2682
+ const hasOgImage = !!(openGraph2 && openGraph2.image);
2683
+ const hasTwitterImage = !!(twitter2 && twitter2.image);
2684
+ if (hasMetaImage && hasOgImage && hasTwitterImage) {
2685
+ score += 5;
2686
+ } else {
2687
+ const missingImages = [];
2688
+ if (!hasMetaImage) missingImages.push("meta image");
2689
+ if (!hasOgImage) missingImages.push("OG image");
2690
+ if (!hasTwitterImage) missingImages.push("Twitter image");
2691
+ issues.push(`Missing images for full score: ${missingImages.join(", ")}`);
2692
+ }
2693
+ const status = getStatusCategory(score);
2694
+ return { score, status, issues };
2695
+ };
1132
2696
 
1133
2697
  // src/index.ts
1134
2698
  var src_default = plugin_default;
1135
2699
 
1136
- exports.allSchemas = types;
1137
- exports.baseMetaSchema = baseMeta;
1138
- exports.createSeoHealthPane = createSeoHealthPane;
1139
- exports.default = src_default;
1140
- exports.metaAttributeSchema = metaAttribute_default;
1141
- exports.metaTagSchema = metaTag_default;
1142
- exports.openGraphSchema = openGraph;
1143
- exports.robotsSchema = robots_default;
1144
- exports.seoFieldsSchema = seoFieldsSchema;
1145
- exports.twitterSchema = twitter;
1146
- //# sourceMappingURL=index.cjs.map
2700
+
2701
+
2702
+
2703
+
2704
+
2705
+
2706
+
2707
+
2708
+
2709
+
2710
+
2711
+
2712
+
2713
+ exports.SchemaOrgDocumentJsonLdPreview = _chunk5XTQRILLcjs.SchemaOrgJsonLdPreview; exports.SchemaOrgJsonLdPreview = _chunk5XTQRILLcjs.SchemaOrgJsonLdPreview; exports.allSchemas = types; exports.baseMetaSchema = baseMeta; exports.calculateHealthScore = calculateHealthScore; exports.createSeoHealthPane = _seofieldspro.createSeoHealthPane; exports.default = src_default; exports.metaAttributeSchema = metaAttribute_default; exports.metaTagSchema = metaTag_default; exports.openGraphSchema = openGraph; exports.robotsSchema = robots_default; exports.seoFieldsSchema = seoFieldsSchema; exports.twitterSchema = twitter;
1147
2714
  //# sourceMappingURL=index.cjs.map