sanity-plugin-seofields 1.8.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.
- package/README.md +437 -26
- package/dist/SeoHealthTool-2COI27KI.cjs +8 -0
- package/dist/SeoHealthTool-2COI27KI.cjs.map +1 -0
- package/dist/SeoHealthTool-CWI3KB2V.js +8 -0
- package/dist/SeoHealthTool-CWI3KB2V.js.map +1 -0
- package/dist/{SeoPreview-3EXR6FD4.cjs → SeoPreview-LMZAWWOE.cjs} +35 -38
- package/dist/SeoPreview-LMZAWWOE.cjs.map +1 -0
- package/dist/{SeoPreview-4PPAF4NO.js → SeoPreview-UNQBKTQO.js} +14 -11
- package/dist/SeoPreview-UNQBKTQO.js.map +1 -0
- package/dist/chunk-3OK3S2F7.cjs +673 -0
- package/dist/chunk-3OK3S2F7.cjs.map +1 -0
- package/dist/{chunk-B5IVI5LP.cjs → chunk-5XTQRILL.cjs} +286 -236
- package/dist/chunk-5XTQRILL.cjs.map +1 -0
- package/dist/{chunk-254YHUN3.cjs → chunk-7N4MLTMR.js} +8 -6
- package/dist/chunk-7N4MLTMR.js.map +1 -0
- package/dist/{chunk-DDAAVRWG.js → chunk-A57XNQC7.cjs} +9 -4
- package/dist/chunk-A57XNQC7.cjs.map +1 -0
- package/dist/chunk-BWLJDK5J.js +624 -0
- package/dist/chunk-BWLJDK5J.js.map +1 -0
- package/dist/chunk-FUUWBQE2.cjs +195 -0
- package/dist/chunk-FUUWBQE2.cjs.map +1 -0
- package/dist/{chunk-ZBHLMQTS.cjs → chunk-HHO2AKAP.js} +44 -17
- package/dist/chunk-HHO2AKAP.js.map +1 -0
- package/dist/chunk-KIEO6QG3.js +195 -0
- package/dist/chunk-KIEO6QG3.js.map +1 -0
- package/dist/chunk-R2U7JF7U.cjs +624 -0
- package/dist/chunk-R2U7JF7U.cjs.map +1 -0
- package/dist/{chunk-VR44E62I.js → chunk-XZKQWV2H.js} +204 -33
- package/dist/chunk-XZKQWV2H.js.map +1 -0
- package/dist/{chunk-HDZZQCH7.js → chunk-Y46ACXM4.cjs} +45 -5
- package/dist/chunk-Y46ACXM4.cjs.map +1 -0
- package/dist/chunk-Z5AOUU4H.js +673 -0
- package/dist/chunk-Z5AOUU4H.js.map +1 -0
- package/dist/cli.js +27 -27
- package/dist/{component-DEXwtemT.d.ts → component-CjT1hvxh.d.ts} +2 -2
- package/dist/{component-BzI-LHPw.d.cts → component-WYh0z8as.d.cts} +2 -2
- package/dist/define-cli.cjs +2 -4
- package/dist/define-cli.cjs.map +1 -1
- package/dist/define-cli.js +4 -4
- package/dist/define-cli.js.map +1 -1
- package/dist/head.cjs +14 -0
- package/dist/head.cjs.map +1 -0
- package/dist/head.d.cts +254 -0
- package/dist/head.d.ts +254 -0
- package/dist/head.js +14 -0
- package/dist/head.js.map +1 -0
- package/dist/index.cjs +978 -367
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -374
- package/dist/index.d.ts +6 -374
- package/dist/index.js +920 -286
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +204 -455
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +8 -206
- package/dist/next.d.ts +8 -206
- package/dist/next.js +181 -111
- package/dist/next.js.map +1 -1
- package/dist/plugin-DQdThyxG.d.ts +449 -0
- package/dist/plugin-DoGeZP79.d.cts +449 -0
- package/dist/schema/next.cjs +173 -327
- package/dist/schema/next.cjs.map +1 -1
- package/dist/schema/next.d.cts +4 -4
- package/dist/schema/next.d.ts +4 -4
- package/dist/schema/next.js +172 -8
- package/dist/schema/next.js.map +1 -1
- package/dist/schema.cjs +376 -517
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +5 -5
- package/dist/schema.d.ts +5 -5
- package/dist/schema.js +216 -11
- package/dist/schema.js.map +1 -1
- package/dist/server.cjs +149 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +102 -0
- package/dist/server.d.ts +102 -0
- package/dist/server.js +149 -0
- package/dist/server.js.map +1 -0
- package/dist/{types-CW8qFdAn.d.ts → types-BAdN1RMu.d.ts} +2 -2
- package/dist/{types-B_vjMPtM.d.cts → types-C5i3KMgs.d.cts} +2 -2
- package/dist/{types-BXSCbl6p.d.cts → types-DP-DiW6f.d.cts} +2 -2
- package/dist/{types-C19cQx5D.d.ts → types-DRLUGLtX.d.ts} +2 -2
- package/dist/{types-yVmQfby9.d.cts → types-DxlPXihz.d.cts} +1 -1
- package/dist/{types-yVmQfby9.d.ts → types-DxlPXihz.d.ts} +1 -1
- package/package.json +25 -4
- package/dist/SeoHealthDashboard-AEKVVMUR-7NWV3C3Y.js +0 -4
- package/dist/SeoHealthDashboard-AEKVVMUR-7NWV3C3Y.js.map +0 -1
- package/dist/SeoHealthDashboard-AEKVVMUR-XWKJUH24.cjs +0 -10
- package/dist/SeoHealthDashboard-AEKVVMUR-XWKJUH24.cjs.map +0 -1
- package/dist/SeoHealthTool-2KKQJLJL.cjs +0 -11
- package/dist/SeoHealthTool-2KKQJLJL.cjs.map +0 -1
- package/dist/SeoHealthTool-3CUKGADZ.js +0 -5
- package/dist/SeoHealthTool-3CUKGADZ.js.map +0 -1
- package/dist/SeoPreview-3EXR6FD4.cjs.map +0 -1
- package/dist/SeoPreview-4PPAF4NO.js.map +0 -1
- package/dist/chunk-254YHUN3.cjs.map +0 -1
- package/dist/chunk-27XSYENH.js +0 -2215
- package/dist/chunk-27XSYENH.js.map +0 -1
- package/dist/chunk-B5IVI5LP.cjs.map +0 -1
- package/dist/chunk-BNXMFD3T.cjs +0 -431
- package/dist/chunk-BNXMFD3T.cjs.map +0 -1
- package/dist/chunk-DDAAVRWG.js.map +0 -1
- package/dist/chunk-HDZZQCH7.js.map +0 -1
- package/dist/chunk-P6IOWIO2.cjs +0 -480
- package/dist/chunk-P6IOWIO2.cjs.map +0 -1
- package/dist/chunk-UOCZFYYP.js +0 -407
- package/dist/chunk-UOCZFYYP.js.map +0 -1
- package/dist/chunk-VR44E62I.js.map +0 -1
- package/dist/chunk-WKXHX3GO.js +0 -424
- package/dist/chunk-WKXHX3GO.js.map +0 -1
- package/dist/chunk-XZDLJ2N6.cjs +0 -2224
- package/dist/chunk-XZDLJ2N6.cjs.map +0 -1
- package/dist/chunk-ZBHLMQTS.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,15 +1,56 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
|
|
1
|
+
import {
|
|
2
|
+
analyzeReadability,
|
|
3
|
+
generateSeoText,
|
|
4
|
+
pickTestOutput
|
|
5
|
+
} from "./chunk-Z5AOUU4H.js";
|
|
6
|
+
import {
|
|
7
|
+
SchemaOrgJsonLdPreview,
|
|
8
|
+
tokenizeHtml
|
|
9
|
+
} from "./chunk-XZKQWV2H.js";
|
|
10
|
+
import {
|
|
11
|
+
getFocusKeywordPlacement,
|
|
12
|
+
getMetaDescriptionValidationMessages,
|
|
13
|
+
getMetaImageValidation,
|
|
14
|
+
getMetaTitleValidationMessages,
|
|
15
|
+
getOgDescriptionValidation,
|
|
16
|
+
getOgImageUrlValidation,
|
|
17
|
+
getOgImageValidation,
|
|
18
|
+
getOgTitleValidation,
|
|
19
|
+
getTwitterDescriptionValidation,
|
|
20
|
+
getTwitterImageUrlValidation,
|
|
21
|
+
getTwitterImageValidation,
|
|
22
|
+
getTwitterTitleValidation,
|
|
23
|
+
hasKeywordOveruse,
|
|
24
|
+
hasMatchingKeyword
|
|
25
|
+
} from "./chunk-HHO2AKAP.js";
|
|
26
|
+
import {
|
|
27
|
+
__spreadProps,
|
|
28
|
+
__spreadValues
|
|
29
|
+
} from "./chunk-7N4MLTMR.js";
|
|
12
30
|
|
|
31
|
+
// src/plugin.ts
|
|
32
|
+
import { createElement as createElement2, lazy as lazy2, Suspense as Suspense2 } from "react";
|
|
33
|
+
import { definePlugin } from "sanity";
|
|
34
|
+
|
|
35
|
+
// src/actions/SeoPublishGateAction.tsx
|
|
36
|
+
import { createSeoPublishGateAction } from "seofields-pro";
|
|
37
|
+
|
|
38
|
+
// src/schemas/index.ts
|
|
39
|
+
import { createElement, lazy, Suspense } from "react";
|
|
40
|
+
import {
|
|
41
|
+
defineField as defineField3,
|
|
42
|
+
defineType as defineType3
|
|
43
|
+
} from "sanity";
|
|
44
|
+
|
|
45
|
+
// src/components/geo/GEOChecklist.tsx
|
|
46
|
+
import { useMemo, useState } from "react";
|
|
47
|
+
import { useFormValue } from "sanity";
|
|
48
|
+
import {
|
|
49
|
+
buildGEOProChecks,
|
|
50
|
+
GEOChecklistProChecks,
|
|
51
|
+
GEOLockedTeaser
|
|
52
|
+
} from "seofields-pro";
|
|
53
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
54
|
var GEOChecklist = (props) => {
|
|
14
55
|
var _a;
|
|
15
56
|
const { path, schemaType } = props;
|
|
@@ -339,23 +380,174 @@ function CheckRow({ check }) {
|
|
|
339
380
|
);
|
|
340
381
|
}
|
|
341
382
|
var GEOChecklist_default = GEOChecklist;
|
|
383
|
+
|
|
384
|
+
// src/components/meta/FocusKeywordInput.tsx
|
|
385
|
+
import { Stack, Text as Text2 } from "@sanity/ui";
|
|
386
|
+
import { useMemo as useMemo2 } from "react";
|
|
387
|
+
import { PatchEvent, set, useFormValue as useFormValue3 } from "sanity";
|
|
388
|
+
|
|
389
|
+
// src/components/ai/SeoGenButton.tsx
|
|
390
|
+
import { SparklesIcon } from "@sanity/icons";
|
|
391
|
+
import { Button, Card, Flex, Text } from "@sanity/ui";
|
|
392
|
+
import { useCallback, useEffect, useRef, useState as useState2 } from "react";
|
|
393
|
+
import { useFormValue as useFormValue2, useWorkspace } from "sanity";
|
|
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
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
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] = useState2("idle");
|
|
442
|
+
const [errorMsg, setErrorMsg] = useState2(null);
|
|
443
|
+
const abortRef = useRef(null);
|
|
444
|
+
const cooldownRef = useRef(null);
|
|
445
|
+
const rootDoc = useFormValue2([]);
|
|
446
|
+
const workspace = useWorkspace();
|
|
447
|
+
const seoObj = useFormValue2([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 = useCallback(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 = pickTestOutput(field);
|
|
469
|
+
} else {
|
|
470
|
+
const controller = new AbortController();
|
|
471
|
+
abortRef.current = controller;
|
|
472
|
+
result = await generateSeoText(field, content, focusKeyword, keywords, {
|
|
473
|
+
config: __spreadProps(__spreadValues({}, 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
|
+
useEffect(() => {
|
|
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__ */ jsxs2(Flex, { direction: "column", gap: 1, align: isFullWidth ? "stretch" : "flex-start", children: [
|
|
508
|
+
/* @__PURE__ */ jsx2(
|
|
509
|
+
Button,
|
|
510
|
+
{
|
|
511
|
+
mode: "ghost",
|
|
512
|
+
tone: "primary",
|
|
513
|
+
paddingX: isFullWidth ? 2 : 3,
|
|
514
|
+
paddingY: 3,
|
|
515
|
+
fontSize: 1,
|
|
516
|
+
icon: SparklesIcon,
|
|
517
|
+
text: buttonText,
|
|
518
|
+
onClick: handleGenerate,
|
|
519
|
+
disabled: isLoading || state === "cooldown"
|
|
520
|
+
}
|
|
521
|
+
),
|
|
522
|
+
!content && /* @__PURE__ */ jsxs2(Text, { size: 0, muted: true, children: [
|
|
523
|
+
"(Set ",
|
|
524
|
+
/* @__PURE__ */ jsx2("code", { children: "ai.content" }),
|
|
525
|
+
" in plugin config for better results)"
|
|
526
|
+
] }),
|
|
527
|
+
state === "error" && errorMsg && /* @__PURE__ */ jsx2(Card, { padding: 2, tone: "critical", radius: 2, children: /* @__PURE__ */ jsx2(Text, { size: 1, children: errorMsg }) })
|
|
528
|
+
] });
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// src/components/meta/FocusKeywordInput.tsx
|
|
532
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
342
533
|
var FocusKeywordInput = (props) => {
|
|
343
534
|
var _a;
|
|
344
|
-
const { value, renderDefault, path } = props;
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
const
|
|
348
|
-
|
|
535
|
+
const { value, renderDefault, path, schemaType, onChange } = props;
|
|
536
|
+
const { options } = schemaType;
|
|
537
|
+
const parent = useFormValue3([path[0]]);
|
|
538
|
+
const rootDoc = useFormValue3([]);
|
|
539
|
+
const feedbackItems = useMemo2(() => {
|
|
540
|
+
var _a2;
|
|
349
541
|
if (!value) return [];
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
const description = ((parent == null ? void 0 : parent.description) || "").toLowerCase();
|
|
542
|
+
const title = (parent == null ? void 0 : parent.title) || "";
|
|
543
|
+
const description = (parent == null ? void 0 : parent.description) || "";
|
|
353
544
|
const slug = ((_a2 = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a2.current) || "";
|
|
545
|
+
const placement = getFocusKeywordPlacement(value, title, description);
|
|
354
546
|
const items = [];
|
|
355
547
|
if (title) {
|
|
356
|
-
if (
|
|
548
|
+
if (placement.atStartOfTitle) {
|
|
357
549
|
items.push({ text: "Keyword at start of title \u2014 excellent", color: "green" });
|
|
358
|
-
} else if (
|
|
550
|
+
} else if (placement.inTitle) {
|
|
359
551
|
items.push({
|
|
360
552
|
text: "Keyword in title \u2014 move it to the start for more prominence",
|
|
361
553
|
color: "orange"
|
|
@@ -365,14 +557,14 @@ var FocusKeywordInput = (props) => {
|
|
|
365
557
|
}
|
|
366
558
|
}
|
|
367
559
|
if (description) {
|
|
368
|
-
if (
|
|
560
|
+
if (placement.inDescription) {
|
|
369
561
|
items.push({ text: "Keyword in meta description", color: "green" });
|
|
370
562
|
} else {
|
|
371
563
|
items.push({ text: "Not in description \u2014 include it naturally", color: "orange" });
|
|
372
564
|
}
|
|
373
565
|
}
|
|
374
566
|
if (slug) {
|
|
375
|
-
const kwSlug =
|
|
567
|
+
const kwSlug = value.toLowerCase().replace(/\s+/g, "-");
|
|
376
568
|
if (slug.includes(kwSlug)) {
|
|
377
569
|
items.push({ text: "Keyword in URL slug", color: "green" });
|
|
378
570
|
} else {
|
|
@@ -382,8 +574,7 @@ var FocusKeywordInput = (props) => {
|
|
|
382
574
|
});
|
|
383
575
|
}
|
|
384
576
|
}
|
|
385
|
-
|
|
386
|
-
if (((_b = title.match(new RegExp(safeKw, "g"))) != null ? _b : []).length > 2) {
|
|
577
|
+
if (placement.isStuffed) {
|
|
387
578
|
items.push({
|
|
388
579
|
text: "Keyword appears too many times in title \u2014 avoid stuffing",
|
|
389
580
|
color: "red"
|
|
@@ -391,10 +582,19 @@ var FocusKeywordInput = (props) => {
|
|
|
391
582
|
}
|
|
392
583
|
return items;
|
|
393
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]);
|
|
394
|
-
return /* @__PURE__ */
|
|
585
|
+
return /* @__PURE__ */ jsxs3(Stack, { space: 3, children: [
|
|
395
586
|
renderDefault(props),
|
|
396
|
-
/* @__PURE__ */
|
|
397
|
-
|
|
587
|
+
/* @__PURE__ */ jsx3(
|
|
588
|
+
SeoGenButton,
|
|
589
|
+
{
|
|
590
|
+
field: "focusKeyword",
|
|
591
|
+
ai: options == null ? void 0 : options.ai,
|
|
592
|
+
seoFieldPath: String(path[0]),
|
|
593
|
+
onGenerate: (v) => onChange(PatchEvent.from(set(v)))
|
|
594
|
+
}
|
|
595
|
+
),
|
|
596
|
+
/* @__PURE__ */ jsx3(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
597
|
+
/* @__PURE__ */ jsx3(
|
|
398
598
|
"div",
|
|
399
599
|
{
|
|
400
600
|
style: {
|
|
@@ -405,87 +605,276 @@ var FocusKeywordInput = (props) => {
|
|
|
405
605
|
}
|
|
406
606
|
}
|
|
407
607
|
),
|
|
408
|
-
/* @__PURE__ */
|
|
608
|
+
/* @__PURE__ */ jsx3(Text2, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
409
609
|
] }, item.text)) })
|
|
410
610
|
] });
|
|
411
611
|
};
|
|
412
612
|
var FocusKeywordInput_default = FocusKeywordInput;
|
|
413
613
|
|
|
414
|
-
// src/
|
|
415
|
-
|
|
614
|
+
// src/components/meta/KeywordsInput.tsx
|
|
615
|
+
import { Stack as Stack3 } from "@sanity/ui";
|
|
616
|
+
import { PatchEvent as PatchEvent2, set as set2 } from "sanity";
|
|
617
|
+
|
|
618
|
+
// src/components/ai/KeywordSuggestions.tsx
|
|
619
|
+
import { SparklesIcon as SparklesIcon2 } from "@sanity/icons";
|
|
620
|
+
import { Badge, Box, Button as Button2, Card as Card2, Flex as Flex2, Spinner, Stack as Stack2, Text as Text3 } from "@sanity/ui";
|
|
621
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
622
|
+
import { useFormValue as useFormValue4, useWorkspace as useWorkspace2 } from "sanity";
|
|
623
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
624
|
+
function KeywordSuggestions({
|
|
625
|
+
ai,
|
|
626
|
+
seoFieldPath,
|
|
627
|
+
existing,
|
|
628
|
+
onAdd,
|
|
629
|
+
onAddAll
|
|
630
|
+
}) {
|
|
416
631
|
var _a;
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
632
|
+
const [panelState, setPanelState] = useState3("idle");
|
|
633
|
+
const [suggestions, setSuggestions] = useState3([]);
|
|
634
|
+
const [errorMsg, setErrorMsg] = useState3(null);
|
|
635
|
+
const abortRef = useRef2(null);
|
|
636
|
+
const rootDoc = useFormValue4([]);
|
|
637
|
+
const workspace = useWorkspace2();
|
|
638
|
+
const seoObj = useFormValue4([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 = useCallback2(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 = pickTestOutput("keywords");
|
|
655
|
+
} else {
|
|
656
|
+
csv = await generateSeoText("keywords", content, focusKeyword, existing, {
|
|
657
|
+
config: __spreadProps(__spreadValues({}, 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 = useCallback2(
|
|
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 = useCallback2(() => {
|
|
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 = useCallback2(() => {
|
|
693
|
+
var _a2;
|
|
694
|
+
(_a2 = abortRef.current) == null ? void 0 : _a2.abort();
|
|
695
|
+
setSuggestions([]);
|
|
696
|
+
setPanelState("idle");
|
|
697
|
+
setErrorMsg(null);
|
|
698
|
+
}, []);
|
|
699
|
+
useEffect2(() => {
|
|
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__ */ jsxs4(Stack2, { space: 2, children: [
|
|
711
|
+
/* @__PURE__ */ jsx4(
|
|
712
|
+
Button2,
|
|
713
|
+
{
|
|
714
|
+
mode: "ghost",
|
|
715
|
+
tone: "primary",
|
|
716
|
+
paddingX: 2,
|
|
717
|
+
paddingY: 3,
|
|
718
|
+
fontSize: 1,
|
|
719
|
+
icon: SparklesIcon2,
|
|
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__ */ jsx4(Card2, { padding: 2, tone: "critical", radius: 2, children: /* @__PURE__ */ jsx4(Text3, { size: 1, children: errorMsg }) })
|
|
725
|
+
] });
|
|
423
726
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
function analyzeReadability(text) {
|
|
431
|
-
const wordList = text.trim().split(/\s+/).filter(Boolean);
|
|
432
|
-
const words = wordList.length;
|
|
433
|
-
if (words < 10) return null;
|
|
434
|
-
const sentences = countSentences(text);
|
|
435
|
-
const syllables = wordList.reduce((acc, word) => acc + countSyllables(word), 0);
|
|
436
|
-
const rawScore = 206.835 - 1.015 * (words / sentences) - 84.6 * (syllables / words);
|
|
437
|
-
const score = Math.round(rawScore * 10) / 10;
|
|
438
|
-
const rawGrade = 0.39 * (words / sentences) + 11.8 * (syllables / words) - 15.59;
|
|
439
|
-
const gradeLevel = Math.round(rawGrade * 10) / 10;
|
|
440
|
-
const avgWordsPerSentence = Math.round(words / sentences * 10) / 10;
|
|
441
|
-
let label;
|
|
442
|
-
let color;
|
|
443
|
-
if (score >= 70) {
|
|
444
|
-
label = "Easy to read";
|
|
445
|
-
color = "green";
|
|
446
|
-
} else if (score >= 50) {
|
|
447
|
-
label = "Fairly easy";
|
|
448
|
-
color = "green";
|
|
449
|
-
} else if (score >= 30) {
|
|
450
|
-
label = "Moderately difficult";
|
|
451
|
-
color = "orange";
|
|
452
|
-
} else {
|
|
453
|
-
label = "Difficult to read";
|
|
454
|
-
color = "red";
|
|
727
|
+
if (panelState === "loading") {
|
|
728
|
+
return /* @__PURE__ */ jsxs4(Flex2, { align: "center", gap: 2, paddingY: 2, children: [
|
|
729
|
+
/* @__PURE__ */ jsx4(Spinner, { muted: true }),
|
|
730
|
+
/* @__PURE__ */ jsx4(Text3, { size: 1, muted: true, children: "Finding keywords\u2026" })
|
|
731
|
+
] });
|
|
455
732
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
733
|
+
const pendingSuggestions = suggestions.filter((kw) => !existing.includes(kw));
|
|
734
|
+
const alreadyAdded = suggestions.filter((kw) => existing.includes(kw));
|
|
735
|
+
return /* @__PURE__ */ jsx4(Card2, { padding: 3, radius: 2, tone: "transparent", border: true, children: /* @__PURE__ */ jsxs4(Stack2, { space: 3, children: [
|
|
736
|
+
/* @__PURE__ */ jsxs4(Flex2, { align: "center", justify: "space-between", children: [
|
|
737
|
+
/* @__PURE__ */ jsx4(Text3, { size: 1, weight: "semibold", muted: true, children: "AI keyword suggestions" }),
|
|
738
|
+
/* @__PURE__ */ jsxs4(Flex2, { gap: 2, children: [
|
|
739
|
+
pendingSuggestions.length > 1 && /* @__PURE__ */ jsx4(
|
|
740
|
+
Button2,
|
|
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__ */ jsx4(
|
|
752
|
+
Button2,
|
|
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__ */ jsxs4(Box, { children: [
|
|
766
|
+
/* @__PURE__ */ jsx4(Text3, { size: 0, muted: true, style: { marginBottom: 8 }, children: "Click to add:" }),
|
|
767
|
+
/* @__PURE__ */ jsx4(Flex2, { gap: 2, wrap: "wrap", style: { marginTop: 6 }, children: pendingSuggestions.map((kw) => /* @__PURE__ */ jsxs4(
|
|
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__ */ jsxs4(Box, { children: [
|
|
800
|
+
/* @__PURE__ */ jsx4(Text3, { size: 0, muted: true, style: { marginBottom: 6 }, children: "Already added:" }),
|
|
801
|
+
/* @__PURE__ */ jsx4(Flex2, { gap: 2, wrap: "wrap", style: { marginTop: 4 }, children: alreadyAdded.map((kw) => /* @__PURE__ */ jsxs4(Badge, { tone: "positive", radius: 5, fontSize: 0, padding: 2, children: [
|
|
802
|
+
"\u2713 ",
|
|
803
|
+
kw
|
|
804
|
+
] }, kw)) })
|
|
805
|
+
] }),
|
|
806
|
+
pendingSuggestions.length === 0 && /* @__PURE__ */ jsx4(Text3, { size: 1, muted: true, children: "All suggested keywords added." })
|
|
807
|
+
] }) });
|
|
465
808
|
}
|
|
809
|
+
|
|
810
|
+
// src/components/meta/KeywordsInput.tsx
|
|
811
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
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(PatchEvent2.from(set2([...existing, kw])));
|
|
819
|
+
};
|
|
820
|
+
const handleAddAll = (keywords) => {
|
|
821
|
+
const merged = [.../* @__PURE__ */ new Set([...existing, ...keywords])];
|
|
822
|
+
onChange(PatchEvent2.from(set2(merged)));
|
|
823
|
+
};
|
|
824
|
+
return /* @__PURE__ */ jsxs5(Stack3, { space: 3, children: [
|
|
825
|
+
renderDefault(props),
|
|
826
|
+
/* @__PURE__ */ jsx5(
|
|
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
|
+
import { Stack as Stack4, Text as Text4 } from "@sanity/ui";
|
|
842
|
+
import { useMemo as useMemo3 } from "react";
|
|
843
|
+
import { PatchEvent as PatchEvent3, set as set3, useFormValue as useFormValue5 } from "sanity";
|
|
844
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
466
845
|
var MetaDescription = (props) => {
|
|
467
|
-
const { value, renderDefault, path } = props;
|
|
468
|
-
const
|
|
846
|
+
const { value, renderDefault, path, schemaType, onChange } = props;
|
|
847
|
+
const { options } = schemaType;
|
|
848
|
+
const parent = useFormValue5([path[0]]);
|
|
469
849
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
470
|
-
const keywords =
|
|
471
|
-
const feedbackItems =
|
|
850
|
+
const keywords = useMemo3(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
851
|
+
const feedbackItems = useMemo3(
|
|
472
852
|
() => getMetaDescriptionValidationMessages(value || "", keywords, isParentseoField),
|
|
473
853
|
[value, keywords, isParentseoField]
|
|
474
854
|
);
|
|
475
|
-
const readability =
|
|
476
|
-
return /* @__PURE__ */
|
|
855
|
+
const readability = useMemo3(() => analyzeReadability(value || ""), [value]);
|
|
856
|
+
return /* @__PURE__ */ jsxs6(Stack4, { space: 3, children: [
|
|
477
857
|
renderDefault(props),
|
|
478
|
-
/* @__PURE__ */
|
|
479
|
-
|
|
858
|
+
/* @__PURE__ */ jsx6(
|
|
859
|
+
SeoGenButton,
|
|
860
|
+
{
|
|
861
|
+
field: "description",
|
|
862
|
+
ai: options == null ? void 0 : options.ai,
|
|
863
|
+
seoFieldPath: String(path[0]),
|
|
864
|
+
onGenerate: (v) => onChange(PatchEvent3.from(set3(v)))
|
|
865
|
+
}
|
|
866
|
+
),
|
|
867
|
+
/* @__PURE__ */ jsx6(Stack4, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
868
|
+
/* @__PURE__ */ jsx6(
|
|
480
869
|
"div",
|
|
481
870
|
{
|
|
482
871
|
style: { width: 10, height: 10, borderRadius: "50%", backgroundColor: item.color }
|
|
483
872
|
}
|
|
484
873
|
),
|
|
485
|
-
/* @__PURE__ */
|
|
874
|
+
/* @__PURE__ */ jsx6(Text4, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
486
875
|
] }, item.text)) }),
|
|
487
|
-
readability && /* @__PURE__ */
|
|
488
|
-
/* @__PURE__ */
|
|
876
|
+
readability && /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
877
|
+
/* @__PURE__ */ jsx6(
|
|
489
878
|
"div",
|
|
490
879
|
{
|
|
491
880
|
style: {
|
|
@@ -496,7 +885,7 @@ var MetaDescription = (props) => {
|
|
|
496
885
|
}
|
|
497
886
|
}
|
|
498
887
|
),
|
|
499
|
-
/* @__PURE__ */
|
|
888
|
+
/* @__PURE__ */ jsxs6(Text4, { weight: "bold", muted: true, size: 14, children: [
|
|
500
889
|
"Readability: ",
|
|
501
890
|
readability.label,
|
|
502
891
|
" (Flesch ",
|
|
@@ -510,18 +899,24 @@ var MetaDescription = (props) => {
|
|
|
510
899
|
] });
|
|
511
900
|
};
|
|
512
901
|
var MetaDescription_default = MetaDescription;
|
|
902
|
+
|
|
903
|
+
// src/components/meta/MetaImage.tsx
|
|
904
|
+
import { Stack as Stack5, Text as Text5 } from "@sanity/ui";
|
|
905
|
+
import { useMemo as useMemo4 } from "react";
|
|
906
|
+
import { useFormValue as useFormValue6 } from "sanity";
|
|
907
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
513
908
|
var MetaImage = (props) => {
|
|
514
909
|
const { value, renderDefault, path } = props;
|
|
515
|
-
const seoParent =
|
|
910
|
+
const seoParent = useFormValue6([path[0]]);
|
|
516
911
|
const hasImage = !!(value == null ? void 0 : value.asset);
|
|
517
|
-
const feedbackItems =
|
|
912
|
+
const feedbackItems = useMemo4(
|
|
518
913
|
() => getMetaImageValidation(hasImage, seoParent),
|
|
519
914
|
[hasImage, seoParent]
|
|
520
915
|
);
|
|
521
|
-
return /* @__PURE__ */
|
|
916
|
+
return /* @__PURE__ */ jsxs7(Stack5, { space: 3, children: [
|
|
522
917
|
renderDefault(props),
|
|
523
|
-
/* @__PURE__ */
|
|
524
|
-
/* @__PURE__ */
|
|
918
|
+
/* @__PURE__ */ jsx7(Stack5, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs7("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
919
|
+
/* @__PURE__ */ jsx7(
|
|
525
920
|
"div",
|
|
526
921
|
{
|
|
527
922
|
style: {
|
|
@@ -532,11 +927,17 @@ var MetaImage = (props) => {
|
|
|
532
927
|
}
|
|
533
928
|
}
|
|
534
929
|
),
|
|
535
|
-
/* @__PURE__ */
|
|
930
|
+
/* @__PURE__ */ jsx7(Text5, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
536
931
|
] }, item.text)) })
|
|
537
932
|
] });
|
|
538
933
|
};
|
|
539
934
|
var MetaImage_default = MetaImage;
|
|
935
|
+
|
|
936
|
+
// src/components/meta/MetaTagsPreview.tsx
|
|
937
|
+
import { Box as Box2, Button as Button3, Card as Card3, Flex as Flex3, Stack as Stack6, Text as Text6 } from "@sanity/ui";
|
|
938
|
+
import { useMemo as useMemo5, useState as useState4 } from "react";
|
|
939
|
+
import { useClient, useColorScheme, useFormValue as useFormValue7 } from "sanity";
|
|
940
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
540
941
|
function resolveImageUrl(asset, projectId, dataset) {
|
|
541
942
|
const ref = asset == null ? void 0 : asset._ref;
|
|
542
943
|
if (!ref) return null;
|
|
@@ -548,13 +949,13 @@ function resolveImageUrl(asset, projectId, dataset) {
|
|
|
548
949
|
}
|
|
549
950
|
var MetaTagsPreview = (props) => {
|
|
550
951
|
const { path } = props;
|
|
551
|
-
const [expanded, setExpanded] =
|
|
552
|
-
const [copyLabel, setCopyLabel] =
|
|
952
|
+
const [expanded, setExpanded] = useState4(false);
|
|
953
|
+
const [copyLabel, setCopyLabel] = useState4("Copy");
|
|
553
954
|
const { scheme } = useColorScheme();
|
|
554
|
-
const seoFields =
|
|
955
|
+
const seoFields = useFormValue7([path[0]]);
|
|
555
956
|
const client = useClient({ apiVersion: "2024-01-01" });
|
|
556
957
|
const { projectId = "", dataset = "" } = client.config();
|
|
557
|
-
const rawHtml =
|
|
958
|
+
const rawHtml = useMemo5(() => {
|
|
558
959
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
559
960
|
const lines = [];
|
|
560
961
|
lines.push('<meta charset="UTF-8" />');
|
|
@@ -610,8 +1011,8 @@ var MetaTagsPreview = (props) => {
|
|
|
610
1011
|
}
|
|
611
1012
|
return lines.join("\n");
|
|
612
1013
|
}, [seoFields, projectId, dataset]);
|
|
613
|
-
const tagCount =
|
|
614
|
-
const tokens =
|
|
1014
|
+
const tagCount = useMemo5(() => rawHtml.split("\n").filter(Boolean).length, [rawHtml]);
|
|
1015
|
+
const tokens = useMemo5(() => tokenizeHtml(rawHtml, scheme), [rawHtml, scheme]);
|
|
615
1016
|
const handleCopy = () => {
|
|
616
1017
|
navigator.clipboard.writeText(rawHtml).then(() => {
|
|
617
1018
|
setCopyLabel("Copied!");
|
|
@@ -624,16 +1025,16 @@ var MetaTagsPreview = (props) => {
|
|
|
624
1025
|
const headerBg = isDark ? "#21262d" : "#f0f2f4";
|
|
625
1026
|
const headerBorder = isDark ? "#30363d" : "#d0d7de";
|
|
626
1027
|
const headerText = isDark ? "#e6edf3" : "#24292f";
|
|
627
|
-
return /* @__PURE__ */
|
|
628
|
-
/* @__PURE__ */
|
|
629
|
-
/* @__PURE__ */
|
|
630
|
-
/* @__PURE__ */
|
|
631
|
-
/* @__PURE__ */
|
|
1028
|
+
return /* @__PURE__ */ jsx8(Card3, { padding: 3, radius: 2, tone: "default", border: true, children: /* @__PURE__ */ jsxs8(Stack6, { space: 3, children: [
|
|
1029
|
+
/* @__PURE__ */ jsxs8(Flex3, { align: "center", justify: "space-between", gap: 3, children: [
|
|
1030
|
+
/* @__PURE__ */ jsx8(Text6, { weight: "semibold", size: 1, children: "Meta Tags Preview" }),
|
|
1031
|
+
/* @__PURE__ */ jsxs8(Flex3, { gap: 2, align: "center", children: [
|
|
1032
|
+
/* @__PURE__ */ jsxs8(Text6, { size: 0, muted: true, children: [
|
|
632
1033
|
tagCount,
|
|
633
1034
|
" tags generated"
|
|
634
1035
|
] }),
|
|
635
|
-
/* @__PURE__ */
|
|
636
|
-
|
|
1036
|
+
/* @__PURE__ */ jsx8(
|
|
1037
|
+
Button3,
|
|
637
1038
|
{
|
|
638
1039
|
text: copyLabel,
|
|
639
1040
|
tone: "default",
|
|
@@ -645,7 +1046,7 @@ var MetaTagsPreview = (props) => {
|
|
|
645
1046
|
)
|
|
646
1047
|
] })
|
|
647
1048
|
] }),
|
|
648
|
-
/* @__PURE__ */
|
|
1049
|
+
/* @__PURE__ */ jsxs8(
|
|
649
1050
|
"button",
|
|
650
1051
|
{
|
|
651
1052
|
type: "button",
|
|
@@ -666,12 +1067,12 @@ var MetaTagsPreview = (props) => {
|
|
|
666
1067
|
color: headerText
|
|
667
1068
|
},
|
|
668
1069
|
children: [
|
|
669
|
-
/* @__PURE__ */
|
|
1070
|
+
/* @__PURE__ */ jsx8("span", { style: { fontSize: 10 }, children: expanded ? "\u25B2" : "\u25BC" }),
|
|
670
1071
|
expanded ? "Hide tags" : "Show tags"
|
|
671
1072
|
]
|
|
672
1073
|
}
|
|
673
1074
|
),
|
|
674
|
-
expanded && /* @__PURE__ */
|
|
1075
|
+
expanded && /* @__PURE__ */ jsxs8(
|
|
675
1076
|
"div",
|
|
676
1077
|
{
|
|
677
1078
|
style: {
|
|
@@ -680,7 +1081,7 @@ var MetaTagsPreview = (props) => {
|
|
|
680
1081
|
overflow: "hidden"
|
|
681
1082
|
},
|
|
682
1083
|
children: [
|
|
683
|
-
/* @__PURE__ */
|
|
1084
|
+
/* @__PURE__ */ jsxs8(
|
|
684
1085
|
"div",
|
|
685
1086
|
{
|
|
686
1087
|
style: {
|
|
@@ -692,7 +1093,7 @@ var MetaTagsPreview = (props) => {
|
|
|
692
1093
|
gap: 6
|
|
693
1094
|
},
|
|
694
1095
|
children: [
|
|
695
|
-
/* @__PURE__ */
|
|
1096
|
+
/* @__PURE__ */ jsx8(
|
|
696
1097
|
"span",
|
|
697
1098
|
{
|
|
698
1099
|
style: {
|
|
@@ -704,12 +1105,12 @@ var MetaTagsPreview = (props) => {
|
|
|
704
1105
|
}
|
|
705
1106
|
}
|
|
706
1107
|
),
|
|
707
|
-
/* @__PURE__ */
|
|
1108
|
+
/* @__PURE__ */ jsx8("span", { style: { fontSize: 11, color: headerText, fontFamily: "monospace" }, children: "HTML" })
|
|
708
1109
|
]
|
|
709
1110
|
}
|
|
710
1111
|
),
|
|
711
|
-
/* @__PURE__ */
|
|
712
|
-
|
|
1112
|
+
/* @__PURE__ */ jsx8(
|
|
1113
|
+
Box2,
|
|
713
1114
|
{
|
|
714
1115
|
as: "pre",
|
|
715
1116
|
padding: 3,
|
|
@@ -724,7 +1125,7 @@ var MetaTagsPreview = (props) => {
|
|
|
724
1125
|
wordBreak: "break-word",
|
|
725
1126
|
backgroundColor: codeBg
|
|
726
1127
|
},
|
|
727
|
-
children: tokens.map((token, idx) => /* @__PURE__ */
|
|
1128
|
+
children: tokens.map((token, idx) => /* @__PURE__ */ jsx8("span", { style: { color: token.color }, children: token.text }, idx))
|
|
728
1129
|
}
|
|
729
1130
|
)
|
|
730
1131
|
]
|
|
@@ -733,20 +1134,26 @@ var MetaTagsPreview = (props) => {
|
|
|
733
1134
|
] }) });
|
|
734
1135
|
};
|
|
735
1136
|
var MetaTagsPreview_default = MetaTagsPreview;
|
|
1137
|
+
|
|
1138
|
+
// src/components/meta/MetaTitle.tsx
|
|
1139
|
+
import { Stack as Stack7, Text as Text7 } from "@sanity/ui";
|
|
1140
|
+
import { useEffect as useEffect3, useMemo as useMemo6, useState as useState5 } from "react";
|
|
1141
|
+
import { PatchEvent as PatchEvent4, set as set4, useClient as useClient2, useFormValue as useFormValue8 } from "sanity";
|
|
1142
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
736
1143
|
var MetaTitle = (props) => {
|
|
737
1144
|
var _a, _b, _c;
|
|
738
|
-
const { value, renderDefault, path, schemaType } = props;
|
|
1145
|
+
const { value, renderDefault, path, schemaType, onChange } = props;
|
|
739
1146
|
const { options } = schemaType;
|
|
740
|
-
const parent =
|
|
1147
|
+
const parent = useFormValue8([path[0]]);
|
|
741
1148
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
742
|
-
const keywords =
|
|
743
|
-
const rootDoc = (_a =
|
|
744
|
-
const client =
|
|
745
|
-
const [groqTitleSuffix, setGroqTitleSuffix] =
|
|
746
|
-
const [isDuplicateTitle, setIsDuplicateTitle] =
|
|
1149
|
+
const keywords = useMemo6(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
1150
|
+
const rootDoc = (_a = useFormValue8([])) != null ? _a : {};
|
|
1151
|
+
const client = useClient2({ apiVersion: (_b = options == null ? void 0 : options.apiVersion) != null ? _b : "2024-01-01" });
|
|
1152
|
+
const [groqTitleSuffix, setGroqTitleSuffix] = useState5("");
|
|
1153
|
+
const [isDuplicateTitle, setIsDuplicateTitle] = useState5(false);
|
|
747
1154
|
const titleSuffixQuery = options == null ? void 0 : options.titleSuffixQuery;
|
|
748
1155
|
const titleSuffixOption = options == null ? void 0 : options.titleSuffix;
|
|
749
|
-
|
|
1156
|
+
useEffect3(() => {
|
|
750
1157
|
if (!titleSuffixQuery) return;
|
|
751
1158
|
client.fetch(titleSuffixQuery).then((result) => {
|
|
752
1159
|
setGroqTitleSuffix(result === null || result === void 0 ? "" : String(result));
|
|
@@ -755,7 +1162,7 @@ var MetaTitle = (props) => {
|
|
|
755
1162
|
});
|
|
756
1163
|
}, [titleSuffixQuery, client]);
|
|
757
1164
|
const currentDocId = (_c = rootDoc._id) != null ? _c : "";
|
|
758
|
-
|
|
1165
|
+
useEffect3(() => {
|
|
759
1166
|
if (!(value == null ? void 0 : value.trim()) || !currentDocId) {
|
|
760
1167
|
setIsDuplicateTitle(false);
|
|
761
1168
|
return () => {
|
|
@@ -769,7 +1176,7 @@ var MetaTitle = (props) => {
|
|
|
769
1176
|
}, 600);
|
|
770
1177
|
return () => clearTimeout(timer);
|
|
771
1178
|
}, [value, client, currentDocId]);
|
|
772
|
-
const resolvedSuffix =
|
|
1179
|
+
const resolvedSuffix = useMemo6(() => {
|
|
773
1180
|
if (titleSuffixQuery) return groqTitleSuffix;
|
|
774
1181
|
if (!titleSuffixOption) return "";
|
|
775
1182
|
if (typeof titleSuffixOption === "function") {
|
|
@@ -778,15 +1185,24 @@ var MetaTitle = (props) => {
|
|
|
778
1185
|
return titleSuffixOption;
|
|
779
1186
|
}, [titleSuffixQuery, groqTitleSuffix, titleSuffixOption, rootDoc]);
|
|
780
1187
|
const suffixLength = resolvedSuffix ? resolvedSuffix.length + 3 : 0;
|
|
781
|
-
const feedbackItems =
|
|
1188
|
+
const feedbackItems = useMemo6(
|
|
782
1189
|
() => getMetaTitleValidationMessages(value || "", keywords, isParentseoField, suffixLength),
|
|
783
1190
|
[value, keywords, isParentseoField, suffixLength]
|
|
784
1191
|
);
|
|
785
|
-
return /* @__PURE__ */
|
|
1192
|
+
return /* @__PURE__ */ jsxs9(Stack7, { space: 3, children: [
|
|
786
1193
|
renderDefault(props),
|
|
787
|
-
/* @__PURE__ */
|
|
788
|
-
|
|
789
|
-
|
|
1194
|
+
/* @__PURE__ */ jsx9(
|
|
1195
|
+
SeoGenButton,
|
|
1196
|
+
{
|
|
1197
|
+
field: "title",
|
|
1198
|
+
ai: options == null ? void 0 : options.ai,
|
|
1199
|
+
seoFieldPath: String(path[0]),
|
|
1200
|
+
onGenerate: (v) => onChange(PatchEvent4.from(set4(v)))
|
|
1201
|
+
}
|
|
1202
|
+
),
|
|
1203
|
+
/* @__PURE__ */ jsxs9(Stack7, { space: 2, children: [
|
|
1204
|
+
feedbackItems.map((item) => /* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1205
|
+
/* @__PURE__ */ jsx9(
|
|
790
1206
|
"div",
|
|
791
1207
|
{
|
|
792
1208
|
style: {
|
|
@@ -797,10 +1213,10 @@ var MetaTitle = (props) => {
|
|
|
797
1213
|
}
|
|
798
1214
|
}
|
|
799
1215
|
),
|
|
800
|
-
/* @__PURE__ */
|
|
1216
|
+
/* @__PURE__ */ jsx9(Text7, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
801
1217
|
] }, item.text)),
|
|
802
|
-
isDuplicateTitle && /* @__PURE__ */
|
|
803
|
-
/* @__PURE__ */
|
|
1218
|
+
isDuplicateTitle && /* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1219
|
+
/* @__PURE__ */ jsx9(
|
|
804
1220
|
"div",
|
|
805
1221
|
{
|
|
806
1222
|
style: {
|
|
@@ -811,7 +1227,7 @@ var MetaTitle = (props) => {
|
|
|
811
1227
|
}
|
|
812
1228
|
}
|
|
813
1229
|
),
|
|
814
|
-
/* @__PURE__ */
|
|
1230
|
+
/* @__PURE__ */ jsx9(Text7, { weight: "bold", muted: true, size: 14, children: "Duplicate meta title \u2014 another published document uses this exact title." })
|
|
815
1231
|
] })
|
|
816
1232
|
] })
|
|
817
1233
|
] });
|
|
@@ -819,6 +1235,10 @@ var MetaTitle = (props) => {
|
|
|
819
1235
|
var MetaTitle_default = MetaTitle;
|
|
820
1236
|
|
|
821
1237
|
// src/utils/fieldsUtils.ts
|
|
1238
|
+
function withLicense(ai, licenseKey) {
|
|
1239
|
+
if (!ai) return void 0;
|
|
1240
|
+
return licenseKey ? __spreadProps(__spreadValues({}, ai), { _licenseKey: licenseKey }) : ai;
|
|
1241
|
+
}
|
|
822
1242
|
var DEFAULT_FIELD_INFO = {
|
|
823
1243
|
title: {
|
|
824
1244
|
title: "Meta Title",
|
|
@@ -956,19 +1376,38 @@ var getFieldHiddenFunction = (fieldName, config) => {
|
|
|
956
1376
|
var isEmpty = (value) => {
|
|
957
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;
|
|
958
1378
|
};
|
|
1379
|
+
|
|
1380
|
+
// src/schemas/types/openGraph/index.ts
|
|
1381
|
+
import { defineField, defineType } from "sanity";
|
|
1382
|
+
|
|
1383
|
+
// src/components/openGraph/OgDescription.tsx
|
|
1384
|
+
import { Stack as Stack8, Text as Text8 } from "@sanity/ui";
|
|
1385
|
+
import { useMemo as useMemo7 } from "react";
|
|
1386
|
+
import { PatchEvent as PatchEvent5, set as set5, useFormValue as useFormValue9 } from "sanity";
|
|
1387
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
959
1388
|
var OgDescription = (props) => {
|
|
960
|
-
const { value, renderDefault, path } = props;
|
|
961
|
-
const
|
|
1389
|
+
const { value, renderDefault, path, schemaType, onChange } = props;
|
|
1390
|
+
const { options } = schemaType;
|
|
1391
|
+
const parent = useFormValue9([path[0]]);
|
|
962
1392
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
963
|
-
const keywords =
|
|
964
|
-
const feedbackItems =
|
|
1393
|
+
const keywords = useMemo7(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
1394
|
+
const feedbackItems = useMemo7(
|
|
965
1395
|
() => getOgDescriptionValidation(value || "", keywords, isParentseoField),
|
|
966
1396
|
[value, keywords, isParentseoField]
|
|
967
1397
|
);
|
|
968
|
-
return /* @__PURE__ */
|
|
1398
|
+
return /* @__PURE__ */ jsxs10(Stack8, { space: 3, children: [
|
|
969
1399
|
renderDefault(props),
|
|
970
|
-
/* @__PURE__ */
|
|
971
|
-
|
|
1400
|
+
/* @__PURE__ */ jsx10(
|
|
1401
|
+
SeoGenButton,
|
|
1402
|
+
{
|
|
1403
|
+
field: "ogDescription",
|
|
1404
|
+
ai: options == null ? void 0 : options.ai,
|
|
1405
|
+
seoFieldPath: String(path[0]),
|
|
1406
|
+
onGenerate: (v) => onChange(PatchEvent5.from(set5(v)))
|
|
1407
|
+
}
|
|
1408
|
+
),
|
|
1409
|
+
/* @__PURE__ */ jsx10(Stack8, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs10("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1410
|
+
/* @__PURE__ */ jsx10(
|
|
972
1411
|
"div",
|
|
973
1412
|
{
|
|
974
1413
|
style: {
|
|
@@ -979,25 +1418,31 @@ var OgDescription = (props) => {
|
|
|
979
1418
|
}
|
|
980
1419
|
}
|
|
981
1420
|
),
|
|
982
|
-
/* @__PURE__ */
|
|
1421
|
+
/* @__PURE__ */ jsx10(Text8, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
983
1422
|
] }, item.text)) })
|
|
984
1423
|
] });
|
|
985
1424
|
};
|
|
986
1425
|
var OgDescription_default = OgDescription;
|
|
1426
|
+
|
|
1427
|
+
// src/components/openGraph/OgImage.tsx
|
|
1428
|
+
import { Stack as Stack9, Text as Text9 } from "@sanity/ui";
|
|
1429
|
+
import { useMemo as useMemo8 } from "react";
|
|
1430
|
+
import { useFormValue as useFormValue10 } from "sanity";
|
|
1431
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
987
1432
|
var OgImage = (props) => {
|
|
988
1433
|
const { value, renderDefault, path } = props;
|
|
989
|
-
const seoParent =
|
|
1434
|
+
const seoParent = useFormValue10([path[0]]);
|
|
990
1435
|
const imgValue = value;
|
|
991
1436
|
const hasImage = !!(imgValue == null ? void 0 : imgValue.asset);
|
|
992
1437
|
const altText = imgValue == null ? void 0 : imgValue.alt;
|
|
993
|
-
const feedbackItems =
|
|
1438
|
+
const feedbackItems = useMemo8(
|
|
994
1439
|
() => getOgImageValidation(hasImage, altText, seoParent),
|
|
995
1440
|
[hasImage, altText, seoParent]
|
|
996
1441
|
);
|
|
997
|
-
return /* @__PURE__ */
|
|
1442
|
+
return /* @__PURE__ */ jsxs11(Stack9, { space: 3, children: [
|
|
998
1443
|
renderDefault(props),
|
|
999
|
-
/* @__PURE__ */
|
|
1000
|
-
/* @__PURE__ */
|
|
1444
|
+
/* @__PURE__ */ jsx11(Stack9, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs11("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1445
|
+
/* @__PURE__ */ jsx11(
|
|
1001
1446
|
"div",
|
|
1002
1447
|
{
|
|
1003
1448
|
style: {
|
|
@@ -1008,19 +1453,25 @@ var OgImage = (props) => {
|
|
|
1008
1453
|
}
|
|
1009
1454
|
}
|
|
1010
1455
|
),
|
|
1011
|
-
/* @__PURE__ */
|
|
1456
|
+
/* @__PURE__ */ jsx11(Text9, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1012
1457
|
] }, item.text)) })
|
|
1013
1458
|
] });
|
|
1014
1459
|
};
|
|
1015
1460
|
var OgImage_default = OgImage;
|
|
1461
|
+
|
|
1462
|
+
// src/components/openGraph/OgImageUrl.tsx
|
|
1463
|
+
import { Stack as Stack10, Text as Text10 } from "@sanity/ui";
|
|
1464
|
+
import { useMemo as useMemo9 } from "react";
|
|
1465
|
+
import { useFormValue as useFormValue11 } from "sanity";
|
|
1466
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1016
1467
|
var OgImageUrl = (props) => {
|
|
1017
1468
|
const { value, renderDefault, path } = props;
|
|
1018
|
-
const seoParent =
|
|
1019
|
-
const feedbackItems =
|
|
1020
|
-
return /* @__PURE__ */
|
|
1469
|
+
const seoParent = useFormValue11([path[0]]);
|
|
1470
|
+
const feedbackItems = useMemo9(() => getOgImageUrlValidation(value, seoParent), [value, seoParent]);
|
|
1471
|
+
return /* @__PURE__ */ jsxs12(Stack10, { space: 3, children: [
|
|
1021
1472
|
renderDefault(props),
|
|
1022
|
-
/* @__PURE__ */
|
|
1023
|
-
/* @__PURE__ */
|
|
1473
|
+
/* @__PURE__ */ jsx12(Stack10, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs12("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1474
|
+
/* @__PURE__ */ jsx12(
|
|
1024
1475
|
"div",
|
|
1025
1476
|
{
|
|
1026
1477
|
style: {
|
|
@@ -1031,24 +1482,40 @@ var OgImageUrl = (props) => {
|
|
|
1031
1482
|
}
|
|
1032
1483
|
}
|
|
1033
1484
|
),
|
|
1034
|
-
/* @__PURE__ */
|
|
1485
|
+
/* @__PURE__ */ jsx12(Text10, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1035
1486
|
] }, item.text)) })
|
|
1036
1487
|
] });
|
|
1037
1488
|
};
|
|
1038
1489
|
var OgImageUrl_default = OgImageUrl;
|
|
1490
|
+
|
|
1491
|
+
// src/components/openGraph/OgTitle.tsx
|
|
1492
|
+
import { Stack as Stack11, Text as Text11 } from "@sanity/ui";
|
|
1493
|
+
import { useMemo as useMemo10 } from "react";
|
|
1494
|
+
import { PatchEvent as PatchEvent6, set as set6, useFormValue as useFormValue12 } from "sanity";
|
|
1495
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1039
1496
|
var OgTitle = (props) => {
|
|
1040
|
-
const { value, renderDefault, path } = props;
|
|
1041
|
-
const
|
|
1497
|
+
const { value, renderDefault, path, schemaType, onChange } = props;
|
|
1498
|
+
const { options } = schemaType;
|
|
1499
|
+
const parent = useFormValue12([path[0]]);
|
|
1042
1500
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
1043
|
-
const keywords =
|
|
1044
|
-
const feedbackItems =
|
|
1501
|
+
const keywords = useMemo10(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
1502
|
+
const feedbackItems = useMemo10(
|
|
1045
1503
|
() => getOgTitleValidation(value || "", keywords, isParentseoField),
|
|
1046
1504
|
[value, keywords, isParentseoField]
|
|
1047
1505
|
);
|
|
1048
|
-
return /* @__PURE__ */
|
|
1506
|
+
return /* @__PURE__ */ jsxs13(Stack11, { space: 3, children: [
|
|
1049
1507
|
renderDefault(props),
|
|
1050
|
-
/* @__PURE__ */
|
|
1051
|
-
|
|
1508
|
+
/* @__PURE__ */ jsx13(
|
|
1509
|
+
SeoGenButton,
|
|
1510
|
+
{
|
|
1511
|
+
field: "ogTitle",
|
|
1512
|
+
ai: options == null ? void 0 : options.ai,
|
|
1513
|
+
seoFieldPath: String(path[0]),
|
|
1514
|
+
onGenerate: (v) => onChange(PatchEvent6.from(set6(v)))
|
|
1515
|
+
}
|
|
1516
|
+
),
|
|
1517
|
+
/* @__PURE__ */ jsx13(Stack11, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs13("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1518
|
+
/* @__PURE__ */ jsx13(
|
|
1052
1519
|
"div",
|
|
1053
1520
|
{
|
|
1054
1521
|
style: {
|
|
@@ -1059,7 +1526,7 @@ var OgTitle = (props) => {
|
|
|
1059
1526
|
}
|
|
1060
1527
|
}
|
|
1061
1528
|
),
|
|
1062
|
-
/* @__PURE__ */
|
|
1529
|
+
/* @__PURE__ */ jsx13(Text11, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1063
1530
|
] }, item.text)) })
|
|
1064
1531
|
] });
|
|
1065
1532
|
};
|
|
@@ -1086,8 +1553,8 @@ function openGraph(config = {}) {
|
|
|
1086
1553
|
hidden: getFieldHiddenFunction("openGraphTitle", config),
|
|
1087
1554
|
components: {
|
|
1088
1555
|
input: OgTitle_default
|
|
1089
|
-
|
|
1090
|
-
}
|
|
1556
|
+
},
|
|
1557
|
+
options: __spreadValues({}, config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {})
|
|
1091
1558
|
})),
|
|
1092
1559
|
defineField(__spreadProps(__spreadValues({
|
|
1093
1560
|
name: "description"
|
|
@@ -1097,8 +1564,8 @@ function openGraph(config = {}) {
|
|
|
1097
1564
|
hidden: getFieldHiddenFunction("openGraphDescription", config),
|
|
1098
1565
|
components: {
|
|
1099
1566
|
input: OgDescription_default
|
|
1100
|
-
|
|
1101
|
-
}
|
|
1567
|
+
},
|
|
1568
|
+
options: __spreadValues({}, config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {})
|
|
1102
1569
|
})),
|
|
1103
1570
|
defineField(__spreadProps(__spreadValues({
|
|
1104
1571
|
name: "siteName"
|
|
@@ -1180,19 +1647,38 @@ function openGraph(config = {}) {
|
|
|
1180
1647
|
]
|
|
1181
1648
|
});
|
|
1182
1649
|
}
|
|
1650
|
+
|
|
1651
|
+
// src/schemas/types/twitter/index.ts
|
|
1652
|
+
import { defineField as defineField2, defineType as defineType2 } from "sanity";
|
|
1653
|
+
|
|
1654
|
+
// src/components/twitter/twitterDescription.tsx
|
|
1655
|
+
import { Stack as Stack12, Text as Text12 } from "@sanity/ui";
|
|
1656
|
+
import { useMemo as useMemo11 } from "react";
|
|
1657
|
+
import { PatchEvent as PatchEvent7, set as set7, useFormValue as useFormValue13 } from "sanity";
|
|
1658
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1183
1659
|
var TwitterDescription = (props) => {
|
|
1184
|
-
const { value, renderDefault, path } = props;
|
|
1185
|
-
const
|
|
1660
|
+
const { value, renderDefault, path, schemaType, onChange } = props;
|
|
1661
|
+
const { options } = schemaType;
|
|
1662
|
+
const parent = useFormValue13([path[0]]);
|
|
1186
1663
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
1187
|
-
const keywords =
|
|
1188
|
-
const feedbackItems =
|
|
1664
|
+
const keywords = useMemo11(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
1665
|
+
const feedbackItems = useMemo11(
|
|
1189
1666
|
() => getTwitterDescriptionValidation(value || "", keywords, isParentseoField),
|
|
1190
1667
|
[value, keywords, isParentseoField]
|
|
1191
1668
|
);
|
|
1192
|
-
return /* @__PURE__ */
|
|
1669
|
+
return /* @__PURE__ */ jsxs14(Stack12, { space: 3, children: [
|
|
1193
1670
|
renderDefault(props),
|
|
1194
|
-
/* @__PURE__ */
|
|
1195
|
-
|
|
1671
|
+
/* @__PURE__ */ jsx14(
|
|
1672
|
+
SeoGenButton,
|
|
1673
|
+
{
|
|
1674
|
+
field: "twitterDescription",
|
|
1675
|
+
ai: options == null ? void 0 : options.ai,
|
|
1676
|
+
seoFieldPath: String(path[0]),
|
|
1677
|
+
onGenerate: (v) => onChange(PatchEvent7.from(set7(v)))
|
|
1678
|
+
}
|
|
1679
|
+
),
|
|
1680
|
+
/* @__PURE__ */ jsx14(Stack12, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs14("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1681
|
+
/* @__PURE__ */ jsx14(
|
|
1196
1682
|
"div",
|
|
1197
1683
|
{
|
|
1198
1684
|
style: {
|
|
@@ -1203,25 +1689,31 @@ var TwitterDescription = (props) => {
|
|
|
1203
1689
|
}
|
|
1204
1690
|
}
|
|
1205
1691
|
),
|
|
1206
|
-
/* @__PURE__ */
|
|
1692
|
+
/* @__PURE__ */ jsx14(Text12, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1207
1693
|
] }, item.text)) })
|
|
1208
1694
|
] });
|
|
1209
1695
|
};
|
|
1210
1696
|
var twitterDescription_default = TwitterDescription;
|
|
1697
|
+
|
|
1698
|
+
// src/components/twitter/TwitterImage.tsx
|
|
1699
|
+
import { Stack as Stack13, Text as Text13 } from "@sanity/ui";
|
|
1700
|
+
import { useMemo as useMemo12 } from "react";
|
|
1701
|
+
import { useFormValue as useFormValue14 } from "sanity";
|
|
1702
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1211
1703
|
var TwitterImage = (props) => {
|
|
1212
1704
|
const { value, renderDefault, path } = props;
|
|
1213
|
-
const seoParent =
|
|
1705
|
+
const seoParent = useFormValue14([path[0]]);
|
|
1214
1706
|
const imgValue = value;
|
|
1215
1707
|
const hasImage = !!(imgValue == null ? void 0 : imgValue.asset);
|
|
1216
1708
|
const altText = imgValue == null ? void 0 : imgValue.alt;
|
|
1217
|
-
const feedbackItems =
|
|
1709
|
+
const feedbackItems = useMemo12(
|
|
1218
1710
|
() => getTwitterImageValidation(hasImage, altText, seoParent),
|
|
1219
1711
|
[hasImage, altText, seoParent]
|
|
1220
1712
|
);
|
|
1221
|
-
return /* @__PURE__ */
|
|
1713
|
+
return /* @__PURE__ */ jsxs15(Stack13, { space: 3, children: [
|
|
1222
1714
|
renderDefault(props),
|
|
1223
|
-
/* @__PURE__ */
|
|
1224
|
-
/* @__PURE__ */
|
|
1715
|
+
/* @__PURE__ */ jsx15(Stack13, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs15("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1716
|
+
/* @__PURE__ */ jsx15(
|
|
1225
1717
|
"div",
|
|
1226
1718
|
{
|
|
1227
1719
|
style: {
|
|
@@ -1232,22 +1724,28 @@ var TwitterImage = (props) => {
|
|
|
1232
1724
|
}
|
|
1233
1725
|
}
|
|
1234
1726
|
),
|
|
1235
|
-
/* @__PURE__ */
|
|
1727
|
+
/* @__PURE__ */ jsx15(Text13, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1236
1728
|
] }, item.text)) })
|
|
1237
1729
|
] });
|
|
1238
1730
|
};
|
|
1239
1731
|
var TwitterImage_default = TwitterImage;
|
|
1732
|
+
|
|
1733
|
+
// src/components/twitter/TwitterImageUrl.tsx
|
|
1734
|
+
import { Stack as Stack14, Text as Text14 } from "@sanity/ui";
|
|
1735
|
+
import { useMemo as useMemo13 } from "react";
|
|
1736
|
+
import { useFormValue as useFormValue15 } from "sanity";
|
|
1737
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1240
1738
|
var TwitterImageUrl = (props) => {
|
|
1241
1739
|
const { value, renderDefault, path } = props;
|
|
1242
|
-
const seoParent =
|
|
1243
|
-
const feedbackItems =
|
|
1740
|
+
const seoParent = useFormValue15([path[0]]);
|
|
1741
|
+
const feedbackItems = useMemo13(
|
|
1244
1742
|
() => getTwitterImageUrlValidation(value, seoParent),
|
|
1245
1743
|
[value, seoParent]
|
|
1246
1744
|
);
|
|
1247
|
-
return /* @__PURE__ */
|
|
1745
|
+
return /* @__PURE__ */ jsxs16(Stack14, { space: 3, children: [
|
|
1248
1746
|
renderDefault(props),
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1747
|
+
/* @__PURE__ */ jsx16(Stack14, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs16("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1748
|
+
/* @__PURE__ */ jsx16(
|
|
1251
1749
|
"div",
|
|
1252
1750
|
{
|
|
1253
1751
|
style: {
|
|
@@ -1258,24 +1756,40 @@ var TwitterImageUrl = (props) => {
|
|
|
1258
1756
|
}
|
|
1259
1757
|
}
|
|
1260
1758
|
),
|
|
1261
|
-
/* @__PURE__ */
|
|
1759
|
+
/* @__PURE__ */ jsx16(Text14, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1262
1760
|
] }, item.text)) })
|
|
1263
1761
|
] });
|
|
1264
1762
|
};
|
|
1265
1763
|
var TwitterImageUrl_default = TwitterImageUrl;
|
|
1764
|
+
|
|
1765
|
+
// src/components/twitter/twitterTitle.tsx
|
|
1766
|
+
import { Stack as Stack15, Text as Text15 } from "@sanity/ui";
|
|
1767
|
+
import { useMemo as useMemo14 } from "react";
|
|
1768
|
+
import { PatchEvent as PatchEvent8, set as set8, useFormValue as useFormValue16 } from "sanity";
|
|
1769
|
+
import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1266
1770
|
var TwitterTitle = (props) => {
|
|
1267
|
-
const { value, renderDefault, path } = props;
|
|
1268
|
-
const
|
|
1771
|
+
const { value, renderDefault, path, schemaType, onChange } = props;
|
|
1772
|
+
const { options } = schemaType;
|
|
1773
|
+
const parent = useFormValue16([path[0]]);
|
|
1269
1774
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
1270
|
-
const keywords =
|
|
1271
|
-
const feedbackItems =
|
|
1775
|
+
const keywords = useMemo14(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
1776
|
+
const feedbackItems = useMemo14(
|
|
1272
1777
|
() => getTwitterTitleValidation(value || "", keywords, isParentseoField),
|
|
1273
1778
|
[value, keywords, isParentseoField]
|
|
1274
1779
|
);
|
|
1275
|
-
return /* @__PURE__ */
|
|
1780
|
+
return /* @__PURE__ */ jsxs17(Stack15, { space: 3, children: [
|
|
1276
1781
|
renderDefault(props),
|
|
1277
|
-
/* @__PURE__ */
|
|
1278
|
-
|
|
1782
|
+
/* @__PURE__ */ jsx17(
|
|
1783
|
+
SeoGenButton,
|
|
1784
|
+
{
|
|
1785
|
+
field: "twitterTitle",
|
|
1786
|
+
ai: options == null ? void 0 : options.ai,
|
|
1787
|
+
seoFieldPath: String(path[0]),
|
|
1788
|
+
onGenerate: (v) => onChange(PatchEvent8.from(set8(v)))
|
|
1789
|
+
}
|
|
1790
|
+
),
|
|
1791
|
+
/* @__PURE__ */ jsx17(Stack15, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs17("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
1792
|
+
/* @__PURE__ */ jsx17(
|
|
1279
1793
|
"div",
|
|
1280
1794
|
{
|
|
1281
1795
|
style: {
|
|
@@ -1286,7 +1800,7 @@ var TwitterTitle = (props) => {
|
|
|
1286
1800
|
}
|
|
1287
1801
|
}
|
|
1288
1802
|
),
|
|
1289
|
-
/* @__PURE__ */
|
|
1803
|
+
/* @__PURE__ */ jsx17(Text15, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1290
1804
|
] }, item.text)) })
|
|
1291
1805
|
] });
|
|
1292
1806
|
};
|
|
@@ -1294,12 +1808,12 @@ var twitterTitle_default = TwitterTitle;
|
|
|
1294
1808
|
|
|
1295
1809
|
// src/schemas/types/twitter/index.ts
|
|
1296
1810
|
function twitter(config = {}) {
|
|
1297
|
-
return
|
|
1811
|
+
return defineType2({
|
|
1298
1812
|
name: "twitter",
|
|
1299
1813
|
title: "X (Formerly Twitter)",
|
|
1300
1814
|
type: "object",
|
|
1301
1815
|
fields: [
|
|
1302
|
-
|
|
1816
|
+
defineField2(__spreadProps(__spreadValues({
|
|
1303
1817
|
name: "card"
|
|
1304
1818
|
}, getFieldInfo("twitterCard", config.fieldOverrides)), {
|
|
1305
1819
|
type: "string",
|
|
@@ -1315,28 +1829,29 @@ function twitter(config = {}) {
|
|
|
1315
1829
|
initialValue: "summary_large_image"
|
|
1316
1830
|
// good default
|
|
1317
1831
|
})),
|
|
1318
|
-
|
|
1832
|
+
defineField2(__spreadProps(__spreadValues({
|
|
1319
1833
|
name: "site"
|
|
1320
1834
|
}, getFieldInfo("twitterSite", config.fieldOverrides)), {
|
|
1321
1835
|
type: "string",
|
|
1322
1836
|
hidden: getFieldHiddenFunction("twitterSite", config)
|
|
1323
1837
|
})),
|
|
1324
|
-
|
|
1838
|
+
defineField2(__spreadProps(__spreadValues({
|
|
1325
1839
|
name: "creator",
|
|
1326
1840
|
type: "string"
|
|
1327
1841
|
}, getFieldInfo("twitterCreator", config.fieldOverrides)), {
|
|
1328
1842
|
hidden: getFieldHiddenFunction("twitterCreator", config)
|
|
1329
1843
|
})),
|
|
1330
|
-
|
|
1844
|
+
defineField2(__spreadProps(__spreadValues({
|
|
1331
1845
|
name: "title",
|
|
1332
1846
|
type: "string"
|
|
1333
1847
|
}, getFieldInfo("twitterTitle", config.fieldOverrides)), {
|
|
1334
1848
|
hidden: getFieldHiddenFunction("twitterTitle", config),
|
|
1335
1849
|
components: {
|
|
1336
1850
|
input: twitterTitle_default
|
|
1337
|
-
}
|
|
1851
|
+
},
|
|
1852
|
+
options: __spreadValues({}, config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {})
|
|
1338
1853
|
})),
|
|
1339
|
-
|
|
1854
|
+
defineField2(__spreadProps(__spreadValues({
|
|
1340
1855
|
name: "description",
|
|
1341
1856
|
type: "text",
|
|
1342
1857
|
rows: 3
|
|
@@ -1344,9 +1859,10 @@ function twitter(config = {}) {
|
|
|
1344
1859
|
hidden: getFieldHiddenFunction("twitterDescription", config),
|
|
1345
1860
|
components: {
|
|
1346
1861
|
input: twitterDescription_default
|
|
1347
|
-
}
|
|
1862
|
+
},
|
|
1863
|
+
options: __spreadValues({}, config.ai ? { ai: withLicense(config.ai, config.licenseKey) } : {})
|
|
1348
1864
|
})),
|
|
1349
|
-
|
|
1865
|
+
defineField2(__spreadProps(__spreadValues({
|
|
1350
1866
|
name: "imageType"
|
|
1351
1867
|
}, getFieldInfo("twitterImageType", config.fieldOverrides)), {
|
|
1352
1868
|
type: "string",
|
|
@@ -1359,7 +1875,7 @@ function twitter(config = {}) {
|
|
|
1359
1875
|
hidden: getFieldHiddenFunction("twitterImage", config),
|
|
1360
1876
|
initialValue: "upload"
|
|
1361
1877
|
})),
|
|
1362
|
-
|
|
1878
|
+
defineField2(__spreadProps(__spreadValues({
|
|
1363
1879
|
name: "image"
|
|
1364
1880
|
}, getFieldInfo("twitterImage", config.fieldOverrides)), {
|
|
1365
1881
|
type: "image",
|
|
@@ -1370,7 +1886,7 @@ function twitter(config = {}) {
|
|
|
1370
1886
|
input: TwitterImage_default
|
|
1371
1887
|
},
|
|
1372
1888
|
fields: [
|
|
1373
|
-
|
|
1889
|
+
defineField2({
|
|
1374
1890
|
name: "alt",
|
|
1375
1891
|
title: "Image Alt Text",
|
|
1376
1892
|
type: "string",
|
|
@@ -1384,7 +1900,7 @@ function twitter(config = {}) {
|
|
|
1384
1900
|
return typeof hiddenFn === "function" ? hiddenFn(context) : hiddenFn;
|
|
1385
1901
|
}
|
|
1386
1902
|
})),
|
|
1387
|
-
|
|
1903
|
+
defineField2(__spreadProps(__spreadValues({
|
|
1388
1904
|
name: "imageUrl"
|
|
1389
1905
|
}, getFieldInfo("twitterImageUrl", config.fieldOverrides)), {
|
|
1390
1906
|
type: "url",
|
|
@@ -1403,7 +1919,7 @@ function twitter(config = {}) {
|
|
|
1403
1919
|
}
|
|
1404
1920
|
|
|
1405
1921
|
// src/schemas/index.ts
|
|
1406
|
-
var LazySeoPreview = lazy(() => import(
|
|
1922
|
+
var LazySeoPreview = lazy(() => import("./SeoPreview-UNQBKTQO.js"));
|
|
1407
1923
|
var SeoPreviewWrapper = (props) => createElement(Suspense, { fallback: null }, createElement(LazySeoPreview, props));
|
|
1408
1924
|
function buildFieldGroupMap(groups) {
|
|
1409
1925
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -1438,18 +1954,86 @@ function withGroup(field, fieldGroupMap) {
|
|
|
1438
1954
|
if (!groups || groups.length === 0) return field;
|
|
1439
1955
|
return __spreadProps(__spreadValues({}, field), { group: groups.length === 1 ? groups[0] : groups });
|
|
1440
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 __spreadValues(__spreadValues(__spreadValues({
|
|
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 __spreadValues(__spreadValues(__spreadValues({}, 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
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1993
|
+
name: "geoChecklist"
|
|
1994
|
+
}, getFieldInfo("geoChecklist", config.fieldOverrides)), {
|
|
1995
|
+
type: "string",
|
|
1996
|
+
readOnly: true,
|
|
1997
|
+
components: {
|
|
1998
|
+
input: GEOChecklist_default
|
|
1999
|
+
},
|
|
2000
|
+
options: __spreadValues({}, 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
|
+
defineField3(__spreadProps(__spreadValues({
|
|
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
|
+
];
|
|
2024
|
+
}
|
|
1441
2025
|
function seoFieldsSchema(config = {}) {
|
|
1442
2026
|
const groupsCfg = config.fieldGroups;
|
|
1443
2027
|
const fieldGroupMap = (groupsCfg == null ? void 0 : groupsCfg.length) ? buildFieldGroupMap(groupsCfg) : void 0;
|
|
1444
2028
|
const sanityGroups = (groupsCfg == null ? void 0 : groupsCfg.length) ? toSanityGroups(groupsCfg) : void 0;
|
|
1445
|
-
return
|
|
2029
|
+
return defineType3(__spreadProps(__spreadValues({
|
|
1446
2030
|
name: "seoFields",
|
|
1447
2031
|
title: "SEO Fields",
|
|
1448
2032
|
type: "object"
|
|
1449
2033
|
}, sanityGroups ? { groups: sanityGroups } : {}), {
|
|
1450
2034
|
fields: [
|
|
1451
2035
|
withGroup(
|
|
1452
|
-
|
|
2036
|
+
defineField3({
|
|
1453
2037
|
name: "robots",
|
|
1454
2038
|
title: "Robots Settings",
|
|
1455
2039
|
type: "robots",
|
|
@@ -1458,16 +2042,14 @@ function seoFieldsSchema(config = {}) {
|
|
|
1458
2042
|
fieldGroupMap
|
|
1459
2043
|
),
|
|
1460
2044
|
// 👇 conditionally spread preview field
|
|
1461
|
-
...
|
|
2045
|
+
...shouldShowPreviewField(config.seoPreview) ? [
|
|
1462
2046
|
withGroup(
|
|
1463
|
-
|
|
2047
|
+
defineField3({
|
|
1464
2048
|
name: "preview",
|
|
1465
2049
|
title: "SEO Preview",
|
|
1466
2050
|
type: "string",
|
|
1467
2051
|
components: { input: SeoPreviewWrapper },
|
|
1468
|
-
options:
|
|
1469
|
-
baseUrl: config.baseUrl || "https://www.example.com"
|
|
1470
|
-
}, 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),
|
|
1471
2053
|
initialValue: "",
|
|
1472
2054
|
readOnly: true
|
|
1473
2055
|
}),
|
|
@@ -1475,20 +2057,20 @@ function seoFieldsSchema(config = {}) {
|
|
|
1475
2057
|
)
|
|
1476
2058
|
] : [],
|
|
1477
2059
|
withGroup(
|
|
1478
|
-
|
|
2060
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1479
2061
|
name: "title"
|
|
1480
2062
|
}, getFieldInfo("title", config.fieldOverrides)), {
|
|
1481
2063
|
type: "string",
|
|
1482
2064
|
components: {
|
|
1483
2065
|
input: MetaTitle_default
|
|
1484
2066
|
},
|
|
1485
|
-
options:
|
|
2067
|
+
options: getTitleFieldOptions(config),
|
|
1486
2068
|
hidden: getFieldHiddenFunction("title", config)
|
|
1487
2069
|
})),
|
|
1488
2070
|
fieldGroupMap
|
|
1489
2071
|
),
|
|
1490
2072
|
withGroup(
|
|
1491
|
-
|
|
2073
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1492
2074
|
name: "description"
|
|
1493
2075
|
}, getFieldInfo("description", config.fieldOverrides)), {
|
|
1494
2076
|
type: "text",
|
|
@@ -1496,12 +2078,13 @@ function seoFieldsSchema(config = {}) {
|
|
|
1496
2078
|
components: {
|
|
1497
2079
|
input: MetaDescription_default
|
|
1498
2080
|
},
|
|
2081
|
+
options: getAiOption(config),
|
|
1499
2082
|
hidden: getFieldHiddenFunction("description", config)
|
|
1500
2083
|
})),
|
|
1501
2084
|
fieldGroupMap
|
|
1502
2085
|
),
|
|
1503
2086
|
withGroup(
|
|
1504
|
-
|
|
2087
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1505
2088
|
name: "metaImage"
|
|
1506
2089
|
}, getFieldInfo("metaImage", config.fieldOverrides)), {
|
|
1507
2090
|
type: "image",
|
|
@@ -1516,7 +2099,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
1516
2099
|
fieldGroupMap
|
|
1517
2100
|
),
|
|
1518
2101
|
withGroup(
|
|
1519
|
-
|
|
2102
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1520
2103
|
name: "metaAttributes"
|
|
1521
2104
|
}, getFieldInfo("metaAttributes", config.fieldOverrides)), {
|
|
1522
2105
|
type: "array",
|
|
@@ -1526,19 +2109,21 @@ function seoFieldsSchema(config = {}) {
|
|
|
1526
2109
|
fieldGroupMap
|
|
1527
2110
|
),
|
|
1528
2111
|
withGroup(
|
|
1529
|
-
|
|
2112
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1530
2113
|
name: "keywords"
|
|
1531
2114
|
}, getFieldInfo("keywords", config.fieldOverrides)), {
|
|
1532
2115
|
title: "Keywords",
|
|
1533
2116
|
type: "array",
|
|
1534
2117
|
of: [{ type: "string" }],
|
|
1535
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),
|
|
1536
2121
|
hidden: getFieldHiddenFunction("keywords", config)
|
|
1537
2122
|
})),
|
|
1538
2123
|
fieldGroupMap
|
|
1539
2124
|
),
|
|
1540
2125
|
withGroup(
|
|
1541
|
-
|
|
2126
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1542
2127
|
name: "canonicalUrl"
|
|
1543
2128
|
}, getFieldInfo("canonicalUrl", config.fieldOverrides)), {
|
|
1544
2129
|
title: "Canonical URL",
|
|
@@ -1549,19 +2134,20 @@ function seoFieldsSchema(config = {}) {
|
|
|
1549
2134
|
fieldGroupMap
|
|
1550
2135
|
),
|
|
1551
2136
|
withGroup(
|
|
1552
|
-
|
|
2137
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1553
2138
|
name: "focusKeyword"
|
|
1554
2139
|
}, getFieldInfo("focusKeyword", config.fieldOverrides)), {
|
|
1555
2140
|
type: "string",
|
|
1556
2141
|
components: {
|
|
1557
2142
|
input: FocusKeywordInput_default
|
|
1558
2143
|
},
|
|
2144
|
+
options: getAiOption(config),
|
|
1559
2145
|
hidden: getFieldHiddenFunction("focusKeyword", config)
|
|
1560
2146
|
})),
|
|
1561
2147
|
fieldGroupMap
|
|
1562
2148
|
),
|
|
1563
2149
|
withGroup(
|
|
1564
|
-
|
|
2150
|
+
defineField3(__spreadProps(__spreadValues({
|
|
1565
2151
|
name: "hreflangs"
|
|
1566
2152
|
}, getFieldInfo("hreflangs", config.fieldOverrides)), {
|
|
1567
2153
|
type: "array",
|
|
@@ -1571,49 +2157,23 @@ function seoFieldsSchema(config = {}) {
|
|
|
1571
2157
|
})),
|
|
1572
2158
|
fieldGroupMap
|
|
1573
2159
|
),
|
|
1574
|
-
...config
|
|
1575
|
-
|
|
1576
|
-
defineField(__spreadProps(__spreadValues({
|
|
1577
|
-
name: "geoChecklist"
|
|
1578
|
-
}, getFieldInfo("geoChecklist", config.fieldOverrides)), {
|
|
1579
|
-
type: "string",
|
|
1580
|
-
readOnly: true,
|
|
1581
|
-
components: {
|
|
1582
|
-
input: GEOChecklist_default
|
|
1583
|
-
},
|
|
1584
|
-
options: __spreadValues({}, config.licenseKey ? { licenseKey: config.licenseKey } : {}),
|
|
1585
|
-
hidden: getFieldHiddenFunction("geoChecklist", config)
|
|
1586
|
-
})),
|
|
1587
|
-
fieldGroupMap
|
|
1588
|
-
)
|
|
1589
|
-
],
|
|
1590
|
-
...config.metaTagsPreview === false ? [] : [
|
|
1591
|
-
withGroup(
|
|
1592
|
-
defineField(__spreadProps(__spreadValues({
|
|
1593
|
-
name: "metaTagsPreview"
|
|
1594
|
-
}, getFieldInfo("metaTagsPreview", config.fieldOverrides)), {
|
|
1595
|
-
type: "string",
|
|
1596
|
-
readOnly: true,
|
|
1597
|
-
components: {
|
|
1598
|
-
input: MetaTagsPreview_default
|
|
1599
|
-
},
|
|
1600
|
-
hidden: getFieldHiddenFunction("metaTagsPreview", config)
|
|
1601
|
-
})),
|
|
1602
|
-
fieldGroupMap
|
|
1603
|
-
)
|
|
1604
|
-
],
|
|
2160
|
+
...buildGeoChecklistField(config, fieldGroupMap),
|
|
2161
|
+
...buildMetaTagsPreviewField(config, fieldGroupMap),
|
|
1605
2162
|
withGroup(openGraph(config), fieldGroupMap),
|
|
1606
2163
|
withGroup(twitter(config), fieldGroupMap)
|
|
1607
2164
|
]
|
|
1608
2165
|
}));
|
|
1609
2166
|
}
|
|
2167
|
+
|
|
2168
|
+
// src/schemas/types/baseMeta/index.ts
|
|
2169
|
+
import { defineField as defineField4, defineType as defineType4 } from "sanity";
|
|
1610
2170
|
function baseMeta(config = {}) {
|
|
1611
|
-
return
|
|
2171
|
+
return defineType4({
|
|
1612
2172
|
name: "baseMeta",
|
|
1613
2173
|
title: "Basic Meta Settings",
|
|
1614
2174
|
type: "object",
|
|
1615
2175
|
fields: [
|
|
1616
|
-
|
|
2176
|
+
defineField4(__spreadProps(__spreadValues({
|
|
1617
2177
|
name: "title"
|
|
1618
2178
|
}, getFieldInfo("title", config.fieldOverrides)), {
|
|
1619
2179
|
type: "string",
|
|
@@ -1622,7 +2182,7 @@ function baseMeta(config = {}) {
|
|
|
1622
2182
|
},
|
|
1623
2183
|
hidden: getFieldHiddenFunction("title", config)
|
|
1624
2184
|
})),
|
|
1625
|
-
|
|
2185
|
+
defineField4(__spreadProps(__spreadValues({
|
|
1626
2186
|
name: "description"
|
|
1627
2187
|
}, getFieldInfo("description", config.fieldOverrides)), {
|
|
1628
2188
|
type: "text",
|
|
@@ -1632,7 +2192,7 @@ function baseMeta(config = {}) {
|
|
|
1632
2192
|
},
|
|
1633
2193
|
hidden: getFieldHiddenFunction("description", config)
|
|
1634
2194
|
})),
|
|
1635
|
-
|
|
2195
|
+
defineField4(__spreadProps(__spreadValues({
|
|
1636
2196
|
name: "metaImage"
|
|
1637
2197
|
}, getFieldInfo("metaImage", config.fieldOverrides)), {
|
|
1638
2198
|
type: "image",
|
|
@@ -1644,7 +2204,7 @@ function baseMeta(config = {}) {
|
|
|
1644
2204
|
},
|
|
1645
2205
|
hidden: getFieldHiddenFunction("metaImage", config)
|
|
1646
2206
|
})),
|
|
1647
|
-
|
|
2207
|
+
defineField4(__spreadProps(__spreadValues({
|
|
1648
2208
|
name: "keywords"
|
|
1649
2209
|
}, getFieldInfo("keywords", config.fieldOverrides)), {
|
|
1650
2210
|
title: "Keywords",
|
|
@@ -1653,7 +2213,7 @@ function baseMeta(config = {}) {
|
|
|
1653
2213
|
description: "Add relevant keywords for this page. These keywords will be used for SEO purposes.",
|
|
1654
2214
|
hidden: getFieldHiddenFunction("keywords", config)
|
|
1655
2215
|
})),
|
|
1656
|
-
|
|
2216
|
+
defineField4(__spreadProps(__spreadValues({
|
|
1657
2217
|
name: "canonicalUrl"
|
|
1658
2218
|
}, getFieldInfo("canonicalUrl", config.fieldOverrides)), {
|
|
1659
2219
|
title: "Canonical URL",
|
|
@@ -1661,7 +2221,7 @@ function baseMeta(config = {}) {
|
|
|
1661
2221
|
description: "Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.",
|
|
1662
2222
|
hidden: getFieldHiddenFunction("canonicalUrl", config)
|
|
1663
2223
|
})),
|
|
1664
|
-
|
|
2224
|
+
defineField4(__spreadProps(__spreadValues({
|
|
1665
2225
|
name: "metaAttributes"
|
|
1666
2226
|
}, getFieldInfo("metaAttributes", config.fieldOverrides)), {
|
|
1667
2227
|
type: "array",
|
|
@@ -1671,12 +2231,15 @@ function baseMeta(config = {}) {
|
|
|
1671
2231
|
]
|
|
1672
2232
|
});
|
|
1673
2233
|
}
|
|
1674
|
-
|
|
2234
|
+
|
|
2235
|
+
// src/schemas/types/hreflang/index.ts
|
|
2236
|
+
import { defineField as defineField5, defineType as defineType5 } from "sanity";
|
|
2237
|
+
var hreflang_default = defineType5({
|
|
1675
2238
|
name: "hreflangEntry",
|
|
1676
2239
|
title: "Hreflang Entry",
|
|
1677
2240
|
type: "object",
|
|
1678
2241
|
fields: [
|
|
1679
|
-
|
|
2242
|
+
defineField5({
|
|
1680
2243
|
name: "locale",
|
|
1681
2244
|
title: "Language / Region",
|
|
1682
2245
|
type: "string",
|
|
@@ -1689,7 +2252,7 @@ var hreflang_default = defineType({
|
|
|
1689
2252
|
return true;
|
|
1690
2253
|
})
|
|
1691
2254
|
}),
|
|
1692
|
-
|
|
2255
|
+
defineField5({
|
|
1693
2256
|
name: "url",
|
|
1694
2257
|
title: "URL",
|
|
1695
2258
|
type: "url",
|
|
@@ -1703,17 +2266,20 @@ var hreflang_default = defineType({
|
|
|
1703
2266
|
}
|
|
1704
2267
|
}
|
|
1705
2268
|
});
|
|
1706
|
-
|
|
2269
|
+
|
|
2270
|
+
// src/schemas/types/metaAttribute/index.ts
|
|
2271
|
+
import { defineField as defineField6, defineType as defineType6 } from "sanity";
|
|
2272
|
+
var metaAttribute_default = defineType6({
|
|
1707
2273
|
name: "metaAttribute",
|
|
1708
2274
|
title: "Meta Attribute",
|
|
1709
2275
|
type: "object",
|
|
1710
2276
|
fields: [
|
|
1711
|
-
|
|
2277
|
+
defineField6({
|
|
1712
2278
|
name: "key",
|
|
1713
2279
|
title: "Attribute Name",
|
|
1714
2280
|
type: "string"
|
|
1715
2281
|
}),
|
|
1716
|
-
|
|
2282
|
+
defineField6({
|
|
1717
2283
|
name: "type",
|
|
1718
2284
|
title: "Attribute Value Type",
|
|
1719
2285
|
type: "string",
|
|
@@ -1725,13 +2291,13 @@ var metaAttribute_default = defineType({
|
|
|
1725
2291
|
},
|
|
1726
2292
|
initialValue: "string"
|
|
1727
2293
|
}),
|
|
1728
|
-
|
|
2294
|
+
defineField6({
|
|
1729
2295
|
name: "value",
|
|
1730
2296
|
title: "Attribute Value",
|
|
1731
2297
|
type: "string",
|
|
1732
2298
|
hidden: ({ parent }) => (parent == null ? void 0 : parent.type) === "image"
|
|
1733
2299
|
}),
|
|
1734
|
-
|
|
2300
|
+
defineField6({
|
|
1735
2301
|
name: "image",
|
|
1736
2302
|
title: "Attribute Image Value",
|
|
1737
2303
|
type: "image",
|
|
@@ -1761,7 +2327,10 @@ var metaAttribute_default = defineType({
|
|
|
1761
2327
|
}
|
|
1762
2328
|
}
|
|
1763
2329
|
});
|
|
1764
|
-
|
|
2330
|
+
|
|
2331
|
+
// src/schemas/types/metaTag/index.ts
|
|
2332
|
+
import { defineType as defineType7 } from "sanity";
|
|
2333
|
+
var metaTag_default = defineType7({
|
|
1765
2334
|
name: "metaTag",
|
|
1766
2335
|
title: "Meta Tag",
|
|
1767
2336
|
type: "object",
|
|
@@ -1775,33 +2344,36 @@ var metaTag_default = defineType({
|
|
|
1775
2344
|
}
|
|
1776
2345
|
]
|
|
1777
2346
|
});
|
|
1778
|
-
|
|
2347
|
+
|
|
2348
|
+
// src/schemas/types/robots/index.ts
|
|
2349
|
+
import { defineField as defineField7, defineType as defineType8 } from "sanity";
|
|
2350
|
+
var robots_default = defineType8({
|
|
1779
2351
|
name: "robots",
|
|
1780
2352
|
title: "Robots Settings",
|
|
1781
2353
|
type: "object",
|
|
1782
2354
|
fields: [
|
|
1783
|
-
|
|
2355
|
+
defineField7({
|
|
1784
2356
|
name: "noIndex",
|
|
1785
2357
|
title: "No Index",
|
|
1786
2358
|
type: "boolean",
|
|
1787
2359
|
initialValue: false,
|
|
1788
2360
|
description: "Enable this to prevent search engines from indexing this page. The page will not appear in search results."
|
|
1789
2361
|
}),
|
|
1790
|
-
|
|
2362
|
+
defineField7({
|
|
1791
2363
|
name: "noFollow",
|
|
1792
2364
|
title: "No Follow",
|
|
1793
2365
|
type: "boolean",
|
|
1794
2366
|
initialValue: false,
|
|
1795
2367
|
description: "Enable this to prevent search engines from following links on this page. Links will not pass SEO value."
|
|
1796
2368
|
}),
|
|
1797
|
-
|
|
2369
|
+
defineField7({
|
|
1798
2370
|
name: "noTranslate",
|
|
1799
2371
|
title: "No Translate",
|
|
1800
2372
|
type: "boolean",
|
|
1801
2373
|
initialValue: false,
|
|
1802
2374
|
description: "Enable this to prevent search engines from offering a translated version of this page in search results."
|
|
1803
2375
|
}),
|
|
1804
|
-
|
|
2376
|
+
defineField7({
|
|
1805
2377
|
name: "noImageIndex",
|
|
1806
2378
|
title: "No Image Index",
|
|
1807
2379
|
type: "boolean",
|
|
@@ -1898,11 +2470,11 @@ var seofields = definePlugin((config = {}) => {
|
|
|
1898
2470
|
const cfg = config;
|
|
1899
2471
|
const { healthDashboard = true, licenseKey, publishGate } = cfg;
|
|
1900
2472
|
const dash = resolveDashboardConfig(healthDashboard, cfg.apiVersion, licenseKey);
|
|
1901
|
-
const LazySeoHealthTool =
|
|
1902
|
-
const BoundSeoHealthTool = () =>
|
|
1903
|
-
|
|
2473
|
+
const LazySeoHealthTool = lazy2(() => import("./SeoHealthTool-CWI3KB2V.js"));
|
|
2474
|
+
const BoundSeoHealthTool = () => createElement2(
|
|
2475
|
+
Suspense2,
|
|
1904
2476
|
{ fallback: null },
|
|
1905
|
-
|
|
2477
|
+
createElement2(LazySeoHealthTool, {
|
|
1906
2478
|
icon: dash.icon,
|
|
1907
2479
|
title: dash.title,
|
|
1908
2480
|
description: dash.description,
|
|
@@ -1957,6 +2529,10 @@ var seofields = definePlugin((config = {}) => {
|
|
|
1957
2529
|
});
|
|
1958
2530
|
var plugin_default = seofields;
|
|
1959
2531
|
|
|
2532
|
+
// src/components/SeoHealthPane.tsx
|
|
2533
|
+
import { createSeoHealthPane } from "seofields-pro";
|
|
2534
|
+
import { createSeoHealthPaneDefault } from "seofields-pro";
|
|
2535
|
+
|
|
1960
2536
|
// src/utils/scoring.ts
|
|
1961
2537
|
var getStatusCategory = (score) => {
|
|
1962
2538
|
if (score >= 80) return "excellent";
|
|
@@ -2025,6 +2601,50 @@ var scoreTwitterCard = (twitter2) => {
|
|
|
2025
2601
|
}
|
|
2026
2602
|
return { score, issues };
|
|
2027
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 (hasMatchingKeyword(title || "", keywords) || hasMatchingKeyword(description || "", keywords)) {
|
|
2611
|
+
score += 3;
|
|
2612
|
+
} else {
|
|
2613
|
+
issues.push("Keywords defined but not used in title or description");
|
|
2614
|
+
}
|
|
2615
|
+
const stuffed = hasKeywordOveruse(title || "", keywords) || hasKeywordOveruse(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 = getFocusKeywordPlacement(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
|
+
};
|
|
2028
2648
|
var calculateHealthScore = (doc) => {
|
|
2029
2649
|
if (!doc.seo) {
|
|
2030
2650
|
return { score: 0, status: "missing", issues: ["SEO fields not configured"] };
|
|
@@ -2033,6 +2653,7 @@ var calculateHealthScore = (doc) => {
|
|
|
2033
2653
|
const title = seo.title;
|
|
2034
2654
|
const description = seo.description;
|
|
2035
2655
|
const keywords = seo.keywords;
|
|
2656
|
+
const focusKeyword = seo.focusKeyword;
|
|
2036
2657
|
const robots = seo.robots;
|
|
2037
2658
|
const openGraph2 = seo.openGraph;
|
|
2038
2659
|
const twitter2 = seo.twitter;
|
|
@@ -2046,8 +2667,9 @@ var calculateHealthScore = (doc) => {
|
|
|
2046
2667
|
issues.push(...descResult.issues);
|
|
2047
2668
|
if (seo.metaImage) score += 10;
|
|
2048
2669
|
else issues.push("Missing meta image");
|
|
2049
|
-
|
|
2050
|
-
|
|
2670
|
+
const keywordResult = scoreKeywordOptimization(keywords, focusKeyword, title, description);
|
|
2671
|
+
score += keywordResult.score;
|
|
2672
|
+
issues.push(...keywordResult.issues);
|
|
2051
2673
|
if (robots && !robots.noIndex) score += 5;
|
|
2052
2674
|
else if (!robots) score += 5;
|
|
2053
2675
|
const ogResult = scoreOpenGraph(openGraph2);
|
|
@@ -2074,7 +2696,19 @@ var calculateHealthScore = (doc) => {
|
|
|
2074
2696
|
|
|
2075
2697
|
// src/index.ts
|
|
2076
2698
|
var src_default = plugin_default;
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2699
|
+
export {
|
|
2700
|
+
SchemaOrgJsonLdPreview as SchemaOrgDocumentJsonLdPreview,
|
|
2701
|
+
SchemaOrgJsonLdPreview,
|
|
2702
|
+
types as allSchemas,
|
|
2703
|
+
baseMeta as baseMetaSchema,
|
|
2704
|
+
calculateHealthScore,
|
|
2705
|
+
createSeoHealthPane,
|
|
2706
|
+
src_default as default,
|
|
2707
|
+
metaAttribute_default as metaAttributeSchema,
|
|
2708
|
+
metaTag_default as metaTagSchema,
|
|
2709
|
+
openGraph as openGraphSchema,
|
|
2710
|
+
robots_default as robotsSchema,
|
|
2711
|
+
seoFieldsSchema,
|
|
2712
|
+
twitter as twitterSchema
|
|
2713
|
+
};
|
|
2080
2714
|
//# sourceMappingURL=index.js.map
|