@focus-reactive/payload-plugin-seo 1.6.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +12 -1
  2. package/dist/admin.css +232 -17
  3. package/dist/components/SeoButton/SeoButtonInner.d.ts.map +1 -1
  4. package/dist/components/SeoButton/SeoButtonInner.js +46 -16
  5. package/dist/components/SeoButton/SeoButtonInner.js.map +1 -1
  6. package/dist/components/SeoDrawer/build-analysis-input.d.ts +2 -2
  7. package/dist/components/SeoDrawer/build-analysis-input.d.ts.map +1 -1
  8. package/dist/components/SeoDrawer/build-analysis-input.js +1 -1
  9. package/dist/components/SeoDrawer/build-analysis-input.js.map +1 -1
  10. package/dist/components/SeoDrawer/buildInput.d.ts +3 -3
  11. package/dist/components/SeoDrawer/buildInput.d.ts.map +1 -1
  12. package/dist/components/SeoDrawer/buildInput.js +3 -2
  13. package/dist/components/SeoDrawer/buildInput.js.map +1 -1
  14. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingIssueBanner.d.ts +7 -0
  15. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingIssueBanner.d.ts.map +1 -0
  16. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingIssueBanner.js +26 -0
  17. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingIssueBanner.js.map +1 -0
  18. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingIssuePills.d.ts +8 -0
  19. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingIssuePills.d.ts.map +1 -0
  20. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingIssuePills.js +17 -0
  21. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingIssuePills.js.map +1 -0
  22. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.d.ts.map +1 -1
  23. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.js +2 -0
  24. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.js.map +1 -1
  25. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.d.ts +1 -0
  26. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.d.ts.map +1 -1
  27. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.js +14 -1
  28. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.js.map +1 -1
  29. package/dist/components/SeoDrawer/components/HeadingsSection/headingIssueCopy.d.ts +7 -0
  30. package/dist/components/SeoDrawer/components/HeadingsSection/headingIssueCopy.d.ts.map +1 -0
  31. package/dist/components/SeoDrawer/components/HeadingsSection/headingIssueCopy.js +26 -0
  32. package/dist/components/SeoDrawer/components/HeadingsSection/headingIssueCopy.js.map +1 -0
  33. package/dist/components/SeoDrawer/components/HeadingsSection/index.d.ts.map +1 -1
  34. package/dist/components/SeoDrawer/components/HeadingsSection/index.js +17 -6
  35. package/dist/components/SeoDrawer/components/HeadingsSection/index.js.map +1 -1
  36. package/dist/components/SeoDrawer/components/SerpPreview/highlight-keyphrase.d.ts +1 -1
  37. package/dist/components/SeoDrawer/components/SerpPreview/highlight-keyphrase.d.ts.map +1 -1
  38. package/dist/components/SeoDrawer/components/SerpPreview/highlight-keyphrase.js +10 -3
  39. package/dist/components/SeoDrawer/components/SerpPreview/highlight-keyphrase.js.map +1 -1
  40. package/dist/components/SeoDrawer/components/SerpPreview/index.d.ts +2 -1
  41. package/dist/components/SeoDrawer/components/SerpPreview/index.d.ts.map +1 -1
  42. package/dist/components/SeoDrawer/components/SerpPreview/index.js +8 -2
  43. package/dist/components/SeoDrawer/components/SerpPreview/index.js.map +1 -1
  44. package/dist/components/SeoDrawer/components/SerpPreview/variants.d.ts +6 -6
  45. package/dist/components/SeoDrawer/components/TabWrapper.d.ts +6 -0
  46. package/dist/components/SeoDrawer/components/TabWrapper.d.ts.map +1 -0
  47. package/dist/components/SeoDrawer/components/TabWrapper.js +8 -0
  48. package/dist/components/SeoDrawer/components/TabWrapper.js.map +1 -0
  49. package/dist/components/SeoDrawer/index.d.ts +11 -4
  50. package/dist/components/SeoDrawer/index.d.ts.map +1 -1
  51. package/dist/components/SeoDrawer/index.js +38 -16
  52. package/dist/components/SeoDrawer/index.js.map +1 -1
  53. package/dist/components/SeoDrawer/keyphraseState.d.ts +16 -0
  54. package/dist/components/SeoDrawer/keyphraseState.d.ts.map +1 -0
  55. package/dist/components/SeoDrawer/keyphraseState.js +73 -0
  56. package/dist/components/SeoDrawer/keyphraseState.js.map +1 -0
  57. package/dist/components/SeoDrawer/keyphraseStorage.d.ts +5 -0
  58. package/dist/components/SeoDrawer/keyphraseStorage.d.ts.map +1 -0
  59. package/dist/components/SeoDrawer/keyphraseStorage.js +60 -0
  60. package/dist/components/SeoDrawer/keyphraseStorage.js.map +1 -0
  61. package/dist/components/SeoDrawer/tabs/InclusiveTab.d.ts.map +1 -1
  62. package/dist/components/SeoDrawer/tabs/InclusiveTab.js +3 -2
  63. package/dist/components/SeoDrawer/tabs/InclusiveTab.js.map +1 -1
  64. package/dist/components/SeoDrawer/tabs/KeyphraseTab.d.ts +14 -7
  65. package/dist/components/SeoDrawer/tabs/KeyphraseTab.d.ts.map +1 -1
  66. package/dist/components/SeoDrawer/tabs/KeyphraseTab.js +110 -69
  67. package/dist/components/SeoDrawer/tabs/KeyphraseTab.js.map +1 -1
  68. package/dist/components/SeoDrawer/tabs/OnPageTab.d.ts.map +1 -1
  69. package/dist/components/SeoDrawer/tabs/OnPageTab.js +3 -2
  70. package/dist/components/SeoDrawer/tabs/OnPageTab.js.map +1 -1
  71. package/dist/components/SeoDrawer/tabs/ReadabilityTab.d.ts.map +1 -1
  72. package/dist/components/SeoDrawer/tabs/ReadabilityTab.js +3 -2
  73. package/dist/components/SeoDrawer/tabs/ReadabilityTab.js.map +1 -1
  74. package/dist/components/SeoDrawer/tabs/SerpTab.d.ts +2 -1
  75. package/dist/components/SeoDrawer/tabs/SerpTab.d.ts.map +1 -1
  76. package/dist/components/SeoDrawer/tabs/SerpTab.js +14 -3
  77. package/dist/components/SeoDrawer/tabs/SerpTab.js.map +1 -1
  78. package/dist/components/SeoDrawer/tabs/VitalsTab.d.ts.map +1 -1
  79. package/dist/components/SeoDrawer/tabs/VitalsTab.js +7 -3
  80. package/dist/components/SeoDrawer/tabs/VitalsTab.js.map +1 -1
  81. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseCard.d.ts +21 -0
  82. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseCard.d.ts.map +1 -0
  83. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseCard.js +38 -0
  84. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseCard.js.map +1 -0
  85. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseDetail.d.ts +25 -0
  86. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseDetail.d.ts.map +1 -0
  87. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseDetail.js +127 -0
  88. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseDetail.js.map +1 -0
  89. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseRail.d.ts +12 -0
  90. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseRail.d.ts.map +1 -0
  91. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseRail.js +54 -0
  92. package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseRail.js.map +1 -0
  93. package/dist/components/SeoDrawer/tabs/keyphrase/SynonymsField.d.ts +7 -0
  94. package/dist/components/SeoDrawer/tabs/keyphrase/SynonymsField.d.ts.map +1 -0
  95. package/dist/components/SeoDrawer/tabs/keyphrase/SynonymsField.js +93 -0
  96. package/dist/components/SeoDrawer/tabs/keyphrase/SynonymsField.js.map +1 -0
  97. package/dist/components/SeoDrawer/tabs/keyphrase/icons.d.ts +2 -0
  98. package/dist/components/SeoDrawer/tabs/keyphrase/icons.d.ts.map +1 -0
  99. package/dist/components/SeoDrawer/tabs/keyphrase/icons.js +7 -0
  100. package/dist/components/SeoDrawer/tabs/keyphrase/icons.js.map +1 -0
  101. package/dist/components/SeoDrawer/useAnalysis.d.ts +0 -1
  102. package/dist/components/SeoDrawer/useAnalysis.d.ts.map +1 -1
  103. package/dist/components/SeoDrawer/useAnalysis.js +0 -3
  104. package/dist/components/SeoDrawer/useAnalysis.js.map +1 -1
  105. package/dist/components/SeoDrawer/useKeyphrases.d.ts +18 -0
  106. package/dist/components/SeoDrawer/useKeyphrases.d.ts.map +1 -0
  107. package/dist/components/SeoDrawer/useKeyphrases.js +78 -0
  108. package/dist/components/SeoDrawer/useKeyphrases.js.map +1 -0
  109. package/dist/components/SeoDrawer/useLiveDocument.d.ts +4 -3
  110. package/dist/components/SeoDrawer/useLiveDocument.d.ts.map +1 -1
  111. package/dist/components/SeoDrawer/useLiveDocument.js +17 -9
  112. package/dist/components/SeoDrawer/useLiveDocument.js.map +1 -1
  113. package/dist/components/SeoDrawer/variants.d.ts +1 -1
  114. package/dist/components/SeoDrawer/variants.d.ts.map +1 -1
  115. package/dist/components/SeoDrawer/variants.js +2 -1
  116. package/dist/components/SeoDrawer/variants.js.map +1 -1
  117. package/dist/constants/index.d.ts +1 -0
  118. package/dist/constants/index.d.ts.map +1 -1
  119. package/dist/constants/index.js +2 -0
  120. package/dist/constants/index.js.map +1 -1
  121. package/dist/engine/buildPaper.d.ts +2 -2
  122. package/dist/engine/buildPaper.d.ts.map +1 -1
  123. package/dist/engine/buildPaper.js +4 -2
  124. package/dist/engine/buildPaper.js.map +1 -1
  125. package/dist/engine/runAnalysis/index.d.ts.map +1 -1
  126. package/dist/engine/runAnalysis/index.js +7 -0
  127. package/dist/engine/runAnalysis/index.js.map +1 -1
  128. package/dist/engine/runAnalysis/services/derive-related.d.ts +4 -0
  129. package/dist/engine/runAnalysis/services/derive-related.d.ts.map +1 -0
  130. package/dist/engine/runAnalysis/services/derive-related.js +16 -0
  131. package/dist/engine/runAnalysis/services/derive-related.js.map +1 -0
  132. package/dist/engine/runAnalysis/services/derive-vitals/heading-issues.d.ts +8 -0
  133. package/dist/engine/runAnalysis/services/derive-vitals/heading-issues.d.ts.map +1 -0
  134. package/dist/engine/runAnalysis/services/derive-vitals/heading-issues.js +37 -0
  135. package/dist/engine/runAnalysis/services/derive-vitals/heading-issues.js.map +1 -0
  136. package/dist/engine/runAnalysis/services/derive-vitals/heading-tree.d.ts.map +1 -1
  137. package/dist/engine/runAnalysis/services/derive-vitals/heading-tree.js +7 -3
  138. package/dist/engine/runAnalysis/services/derive-vitals/heading-tree.js.map +1 -1
  139. package/dist/engine/types/analysis.d.ts +23 -0
  140. package/dist/engine/types/analysis.d.ts.map +1 -1
  141. package/dist/ui/Button.d.ts +9 -0
  142. package/dist/ui/Button.d.ts.map +1 -0
  143. package/dist/ui/Button.js +34 -0
  144. package/dist/ui/Button.js.map +1 -0
  145. package/dist/ui/ScoreRing.d.ts +17 -4
  146. package/dist/ui/ScoreRing.d.ts.map +1 -1
  147. package/dist/ui/ScoreRing.js +38 -14
  148. package/dist/ui/ScoreRing.js.map +1 -1
  149. package/package.json +1 -1
  150. package/dist/components/SeoDrawer/keyphrasePending.d.ts +0 -2
  151. package/dist/components/SeoDrawer/keyphrasePending.d.ts.map +0 -1
  152. package/dist/components/SeoDrawer/keyphrasePending.js +0 -9
  153. package/dist/components/SeoDrawer/keyphrasePending.js.map +0 -1
@@ -6,11 +6,14 @@ import { resolveContentExtractor } from "../../content/registry";
6
6
  import { buildAnalysisInput } from "./build-analysis-input";
7
7
  const erroredPaths = /* @__PURE__ */ new Set();
8
8
  const DEBOUNCE_MS = 1e3;
9
+ function keyphraseSignature(keyphrases) {
10
+ return JSON.stringify(keyphrases.map((k) => [k.text, k.synonyms]));
11
+ }
9
12
  function useLiveDocument({
10
13
  collectionSlug: _collectionSlug,
11
14
  fields,
12
15
  site,
13
- keyphrase,
16
+ keyphrases,
14
17
  enabled = true,
15
18
  extractContentPath
16
19
  }) {
@@ -18,21 +21,25 @@ function useLiveDocument({
18
21
  const locale = useLocale();
19
22
  const { config } = useConfig();
20
23
  const debouncedFields = useDebounce(formFields, DEBOUNCE_MS);
21
- const debouncedKeyphrase = useDebounce(keyphrase, DEBOUNCE_MS);
24
+ const debouncedKeyphrases = useDebounce(keyphrases, DEBOUNCE_MS);
22
25
  const apiRoute = config.routes.api;
23
26
  const values = useMemo(
24
27
  () => enabled ? reduceFieldsToValues(debouncedFields, true) : {},
25
28
  [enabled, debouncedFields]
26
29
  );
27
30
  const signature = useMemo(
28
- () => JSON.stringify({ values, keyphrase: debouncedKeyphrase, locale: locale?.code ?? null }),
29
- [values, debouncedKeyphrase, locale]
31
+ () => JSON.stringify({
32
+ values,
33
+ keyphrases: keyphraseSignature(debouncedKeyphrases),
34
+ locale: locale?.code ?? null
35
+ }),
36
+ [values, debouncedKeyphrases, locale]
30
37
  );
31
38
  const liveRef = useRef({
32
39
  formFields,
33
40
  values,
34
- keyphrase,
35
- debouncedKeyphrase,
41
+ keyphrases,
42
+ debouncedKeyphrases,
36
43
  locale,
37
44
  fields,
38
45
  site,
@@ -42,8 +49,8 @@ function useLiveDocument({
42
49
  liveRef.current = {
43
50
  formFields,
44
51
  values,
45
- keyphrase,
46
- debouncedKeyphrase,
52
+ keyphrases,
53
+ debouncedKeyphrases,
47
54
  locale,
48
55
  fields,
49
56
  site,
@@ -66,7 +73,7 @@ function useLiveDocument({
66
73
  locale: s.locale,
67
74
  payloadLocale: s.locale?.code,
68
75
  apiRoute: s.apiRoute,
69
- keyphrase: live ? s.keyphrase : s.debouncedKeyphrase,
76
+ keyphrases: live ? s.keyphrases : s.debouncedKeyphrases,
70
77
  fields: s.fields,
71
78
  site: s.site,
72
79
  extractor
@@ -77,6 +84,7 @@ function useLiveDocument({
77
84
  return { signature, getInput };
78
85
  }
79
86
  export {
87
+ keyphraseSignature,
80
88
  useLiveDocument
81
89
  };
82
90
  //# sourceMappingURL=useLiveDocument.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/SeoDrawer/useLiveDocument.ts"],"sourcesContent":["\"use client\";\n\nimport { useAllFormFields, useConfig, useDebounce, useLocale } from \"@payloadcms/ui\";\nimport { reduceFieldsToValues } from \"payload/shared\";\nimport { useCallback, useMemo, useRef } from \"react\";\nimport { resolveContentExtractor } from \"../../content/registry\";\nimport type { AnalysisInput } from \"../../engine/types/analysis\";\nimport type { SeoFieldPaths } from \"../../types/config\";\nimport { buildAnalysisInput } from \"./build-analysis-input\";\n\nconst erroredPaths = new Set<string>();\nconst DEBOUNCE_MS = 1000;\n\nexport interface LiveDocArgs {\n collectionSlug: string;\n fields: SeoFieldPaths;\n site: { name: string; baseUrl: string };\n keyphrase: string;\n enabled?: boolean;\n extractContentPath: string;\n}\n\nexport interface UseLiveDocumentResult {\n signature: string;\n getInput: (opts?: { live?: boolean }) => Promise<AnalysisInput>;\n}\n\nexport function useLiveDocument({\n collectionSlug: _collectionSlug,\n fields,\n site,\n keyphrase,\n enabled = true,\n extractContentPath,\n}: LiveDocArgs): UseLiveDocumentResult {\n const [formFields] = useAllFormFields();\n const locale = useLocale();\n const { config } = useConfig();\n\n const debouncedFields = useDebounce(formFields, DEBOUNCE_MS);\n const debouncedKeyphrase = useDebounce(keyphrase, DEBOUNCE_MS);\n const apiRoute = config.routes.api;\n\n const values = useMemo<Record<string, unknown>>(\n () => (enabled ? (reduceFieldsToValues(debouncedFields, true) as Record<string, unknown>) : {}),\n [enabled, debouncedFields]\n );\n\n const signature = useMemo(\n () => JSON.stringify({ values, keyphrase: debouncedKeyphrase, locale: locale?.code ?? null }),\n [values, debouncedKeyphrase, locale]\n );\n\n const liveRef = useRef({\n formFields,\n values,\n keyphrase,\n debouncedKeyphrase,\n locale,\n fields,\n site,\n extractContentPath,\n apiRoute,\n });\n liveRef.current = {\n formFields,\n values,\n keyphrase,\n debouncedKeyphrase,\n locale,\n fields,\n site,\n extractContentPath,\n apiRoute,\n };\n\n const getInput = useCallback(\n async ({ live = false }: { live?: boolean } = {}): Promise<AnalysisInput> => {\n const s = liveRef.current;\n const inputValues = live\n ? (reduceFieldsToValues(s.formFields, true) as Record<string, unknown>)\n : s.values;\n\n const extractor = resolveContentExtractor(s.extractContentPath);\n if (!extractor && !erroredPaths.has(s.extractContentPath)) {\n erroredPaths.add(s.extractContentPath);\n console.error(\n `[payload-plugin-seo] extractContentPath \"${s.extractContentPath}\" is not registered; content analysis will be empty. Call registerContentExtractors from \"@focus-reactive/payload-plugin-seo/content\" in an admin-mounted client module.`\n );\n }\n\n return buildAnalysisInput({\n values: inputValues,\n locale: s.locale,\n payloadLocale: s.locale?.code,\n apiRoute: s.apiRoute,\n keyphrase: live ? s.keyphrase : s.debouncedKeyphrase,\n fields: s.fields,\n site: s.site,\n extractor,\n });\n },\n []\n );\n\n return { signature, getInput };\n}\n"],"mappings":";AAEA,SAAS,kBAAkB,WAAW,aAAa,iBAAiB;AACpE,SAAS,4BAA4B;AACrC,SAAS,aAAa,SAAS,cAAc;AAC7C,SAAS,+BAA+B;AAGxC,SAAS,0BAA0B;AAEnC,MAAM,eAAe,oBAAI,IAAY;AACrC,MAAM,cAAc;AAgBb,SAAS,gBAAgB;AAAA,EAC9B,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AACF,GAAuC;AACrC,QAAM,CAAC,UAAU,IAAI,iBAAiB;AACtC,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,OAAO,IAAI,UAAU;AAE7B,QAAM,kBAAkB,YAAY,YAAY,WAAW;AAC3D,QAAM,qBAAqB,YAAY,WAAW,WAAW;AAC7D,QAAM,WAAW,OAAO,OAAO;AAE/B,QAAM,SAAS;AAAA,IACb,MAAO,UAAW,qBAAqB,iBAAiB,IAAI,IAAgC,CAAC;AAAA,IAC7F,CAAC,SAAS,eAAe;AAAA,EAC3B;AAEA,QAAM,YAAY;AAAA,IAChB,MAAM,KAAK,UAAU,EAAE,QAAQ,WAAW,oBAAoB,QAAQ,QAAQ,QAAQ,KAAK,CAAC;AAAA,IAC5F,CAAC,QAAQ,oBAAoB,MAAM;AAAA,EACrC;AAEA,QAAM,UAAU,OAAO;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,UAAQ,UAAU;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,WAAW;AAAA,IACf,OAAO,EAAE,OAAO,MAAM,IAAwB,CAAC,MAA8B;AAC3E,YAAM,IAAI,QAAQ;AAClB,YAAM,cAAc,OACf,qBAAqB,EAAE,YAAY,IAAI,IACxC,EAAE;AAEN,YAAM,YAAY,wBAAwB,EAAE,kBAAkB;AAC9D,UAAI,CAAC,aAAa,CAAC,aAAa,IAAI,EAAE,kBAAkB,GAAG;AACzD,qBAAa,IAAI,EAAE,kBAAkB;AACrC,gBAAQ;AAAA,UACN,4CAA4C,EAAE,kBAAkB;AAAA,QAClE;AAAA,MACF;AAEA,aAAO,mBAAmB;AAAA,QACxB,QAAQ;AAAA,QACR,QAAQ,EAAE;AAAA,QACV,eAAe,EAAE,QAAQ;AAAA,QACzB,UAAU,EAAE;AAAA,QACZ,WAAW,OAAO,EAAE,YAAY,EAAE;AAAA,QAClC,QAAQ,EAAE;AAAA,QACV,MAAM,EAAE;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,WAAW,SAAS;AAC/B;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/SeoDrawer/useLiveDocument.ts"],"sourcesContent":["\"use client\";\n\nimport { useAllFormFields, useConfig, useDebounce, useLocale } from \"@payloadcms/ui\";\nimport { reduceFieldsToValues } from \"payload/shared\";\nimport { useCallback, useMemo, useRef } from \"react\";\nimport { resolveContentExtractor } from \"../../content/registry\";\nimport type { AnalysisInput, KeyphraseInput } from \"../../engine/types/analysis\";\nimport type { SeoFieldPaths } from \"../../types/config\";\nimport { buildAnalysisInput } from \"./build-analysis-input\";\n\nconst erroredPaths = new Set<string>();\nconst DEBOUNCE_MS = 1000;\n\nexport function keyphraseSignature(keyphrases: KeyphraseInput[]): string {\n return JSON.stringify(keyphrases.map((k) => [k.text, k.synonyms]));\n}\n\nexport interface LiveDocArgs {\n collectionSlug: string;\n fields: SeoFieldPaths;\n site: { name: string; baseUrl: string };\n keyphrases: KeyphraseInput[];\n enabled?: boolean;\n extractContentPath: string;\n}\n\nexport interface UseLiveDocumentResult {\n signature: string;\n getInput: (opts?: { live?: boolean }) => Promise<AnalysisInput>;\n}\n\nexport function useLiveDocument({\n collectionSlug: _collectionSlug,\n fields,\n site,\n keyphrases,\n enabled = true,\n extractContentPath,\n}: LiveDocArgs): UseLiveDocumentResult {\n const [formFields] = useAllFormFields();\n const locale = useLocale();\n const { config } = useConfig();\n\n const debouncedFields = useDebounce(formFields, DEBOUNCE_MS);\n const debouncedKeyphrases = useDebounce(keyphrases, DEBOUNCE_MS);\n const apiRoute = config.routes.api;\n\n const values = useMemo<Record<string, unknown>>(\n () => (enabled ? (reduceFieldsToValues(debouncedFields, true) as Record<string, unknown>) : {}),\n [enabled, debouncedFields]\n );\n\n const signature = useMemo(\n () =>\n JSON.stringify({\n values,\n keyphrases: keyphraseSignature(debouncedKeyphrases),\n locale: locale?.code ?? null,\n }),\n [values, debouncedKeyphrases, locale]\n );\n\n const liveRef = useRef({\n formFields,\n values,\n keyphrases,\n debouncedKeyphrases,\n locale,\n fields,\n site,\n extractContentPath,\n apiRoute,\n });\n liveRef.current = {\n formFields,\n values,\n keyphrases,\n debouncedKeyphrases,\n locale,\n fields,\n site,\n extractContentPath,\n apiRoute,\n };\n\n const getInput = useCallback(\n async ({ live = false }: { live?: boolean } = {}): Promise<AnalysisInput> => {\n const s = liveRef.current;\n const inputValues = live\n ? (reduceFieldsToValues(s.formFields, true) as Record<string, unknown>)\n : s.values;\n\n const extractor = resolveContentExtractor(s.extractContentPath);\n if (!extractor && !erroredPaths.has(s.extractContentPath)) {\n erroredPaths.add(s.extractContentPath);\n console.error(\n `[payload-plugin-seo] extractContentPath \"${s.extractContentPath}\" is not registered; content analysis will be empty. Call registerContentExtractors from \"@focus-reactive/payload-plugin-seo/content\" in an admin-mounted client module.`\n );\n }\n\n return buildAnalysisInput({\n values: inputValues,\n locale: s.locale,\n payloadLocale: s.locale?.code,\n apiRoute: s.apiRoute,\n keyphrases: live ? s.keyphrases : s.debouncedKeyphrases,\n fields: s.fields,\n site: s.site,\n extractor,\n });\n },\n []\n );\n\n return { signature, getInput };\n}\n"],"mappings":";AAEA,SAAS,kBAAkB,WAAW,aAAa,iBAAiB;AACpE,SAAS,4BAA4B;AACrC,SAAS,aAAa,SAAS,cAAc;AAC7C,SAAS,+BAA+B;AAGxC,SAAS,0BAA0B;AAEnC,MAAM,eAAe,oBAAI,IAAY;AACrC,MAAM,cAAc;AAEb,SAAS,mBAAmB,YAAsC;AACvE,SAAO,KAAK,UAAU,WAAW,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AACnE;AAgBO,SAAS,gBAAgB;AAAA,EAC9B,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AACF,GAAuC;AACrC,QAAM,CAAC,UAAU,IAAI,iBAAiB;AACtC,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,OAAO,IAAI,UAAU;AAE7B,QAAM,kBAAkB,YAAY,YAAY,WAAW;AAC3D,QAAM,sBAAsB,YAAY,YAAY,WAAW;AAC/D,QAAM,WAAW,OAAO,OAAO;AAE/B,QAAM,SAAS;AAAA,IACb,MAAO,UAAW,qBAAqB,iBAAiB,IAAI,IAAgC,CAAC;AAAA,IAC7F,CAAC,SAAS,eAAe;AAAA,EAC3B;AAEA,QAAM,YAAY;AAAA,IAChB,MACE,KAAK,UAAU;AAAA,MACb;AAAA,MACA,YAAY,mBAAmB,mBAAmB;AAAA,MAClD,QAAQ,QAAQ,QAAQ;AAAA,IAC1B,CAAC;AAAA,IACH,CAAC,QAAQ,qBAAqB,MAAM;AAAA,EACtC;AAEA,QAAM,UAAU,OAAO;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,UAAQ,UAAU;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,WAAW;AAAA,IACf,OAAO,EAAE,OAAO,MAAM,IAAwB,CAAC,MAA8B;AAC3E,YAAM,IAAI,QAAQ;AAClB,YAAM,cAAc,OACf,qBAAqB,EAAE,YAAY,IAAI,IACxC,EAAE;AAEN,YAAM,YAAY,wBAAwB,EAAE,kBAAkB;AAC9D,UAAI,CAAC,aAAa,CAAC,aAAa,IAAI,EAAE,kBAAkB,GAAG;AACzD,qBAAa,IAAI,EAAE,kBAAkB;AACrC,gBAAQ;AAAA,UACN,4CAA4C,EAAE,kBAAkB;AAAA,QAClE;AAAA,MACF;AAEA,aAAO,mBAAmB;AAAA,QACxB,QAAQ;AAAA,QACR,QAAQ,EAAE;AAAA,QACV,eAAe,EAAE,QAAQ;AAAA,QACzB,UAAU,EAAE;AAAA,QACZ,YAAY,OAAO,EAAE,aAAa,EAAE;AAAA,QACpC,QAAQ,EAAE;AAAA,QACV,MAAM,EAAE;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,WAAW,SAAS;AAC/B;","names":[]}
@@ -1,4 +1,4 @@
1
1
  export declare const statusVar: (props?: ({
2
- status?: "good" | "warn" | "bad" | null | undefined;
2
+ status?: "good" | "warn" | "bad" | "idle" | null | undefined;
3
3
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
4
4
  //# sourceMappingURL=variants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"variants.d.ts","sourceRoot":"","sources":["../../../src/components/SeoDrawer/variants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;;8EAQpB,CAAC"}
1
+ {"version":3,"file":"variants.d.ts","sourceRoot":"","sources":["../../../src/components/SeoDrawer/variants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;;8EASpB,CAAC"}
@@ -4,7 +4,8 @@ const statusVar = cva("", {
4
4
  status: {
5
5
  good: "[--seo-c:var(--color-seo-good)]",
6
6
  warn: "[--seo-c:var(--color-seo-warn)]",
7
- bad: "[--seo-c:var(--color-seo-bad)]"
7
+ bad: "[--seo-c:var(--color-seo-bad)]",
8
+ idle: "[--seo-c:var(--color-neutral-300)]"
8
9
  }
9
10
  }
10
11
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/SeoDrawer/variants.ts"],"sourcesContent":["import { cva } from \"class-variance-authority\";\n\nexport const statusVar = cva(\"\", {\n variants: {\n status: {\n good: \"[--seo-c:var(--color-seo-good)]\",\n warn: \"[--seo-c:var(--color-seo-warn)]\",\n bad: \"[--seo-c:var(--color-seo-bad)]\",\n },\n },\n});\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,YAAY,IAAI,IAAI;AAAA,EAC/B,UAAU;AAAA,IACR,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK;AAAA,IACP;AAAA,EACF;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/SeoDrawer/variants.ts"],"sourcesContent":["import { cva } from \"class-variance-authority\";\n\nexport const statusVar = cva(\"\", {\n variants: {\n status: {\n good: \"[--seo-c:var(--color-seo-good)]\",\n warn: \"[--seo-c:var(--color-seo-warn)]\",\n bad: \"[--seo-c:var(--color-seo-bad)]\",\n idle: \"[--seo-c:var(--color-neutral-300)]\",\n },\n },\n});\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,YAAY,IAAI,IAAI;AAAA,EAC/B,UAAU;AAAA,IACR,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK;AAAA,MACL,MAAM;AAAA,IACR;AAAA,EACF;AACF,CAAC;","names":[]}
@@ -1,5 +1,6 @@
1
1
  export declare const PLUGIN_NAME = "payload-plugin-seo";
2
2
  export declare const PKG = "@focus-reactive/payload-plugin-seo";
3
3
  export declare const DEFAULT_LOCALES: readonly ["en"];
4
+ export declare const MAX_KEYPHRASES = 5;
4
5
  export * from "./statuses";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,uBAAuB,CAAC;AAChD,eAAO,MAAM,GAAG,uCAAuC,CAAC;AAExD,eAAO,MAAM,eAAe,iBAAkB,CAAC;AAE/C,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,uBAAuB,CAAC;AAChD,eAAO,MAAM,GAAG,uCAAuC,CAAC;AAExD,eAAO,MAAM,eAAe,iBAAkB,CAAC;AAE/C,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,cAAc,YAAY,CAAC"}
@@ -1,9 +1,11 @@
1
1
  const PLUGIN_NAME = "payload-plugin-seo";
2
2
  const PKG = "@focus-reactive/payload-plugin-seo";
3
3
  const DEFAULT_LOCALES = ["en"];
4
+ const MAX_KEYPHRASES = 5;
4
5
  export * from "./statuses";
5
6
  export {
6
7
  DEFAULT_LOCALES,
8
+ MAX_KEYPHRASES,
7
9
  PKG,
8
10
  PLUGIN_NAME
9
11
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constants/index.ts"],"sourcesContent":["export const PLUGIN_NAME = \"payload-plugin-seo\";\nexport const PKG = \"@focus-reactive/payload-plugin-seo\";\n\nexport const DEFAULT_LOCALES = [\"en\"] as const;\n\nexport * from \"./statuses\";\n"],"mappings":"AAAO,MAAM,cAAc;AACpB,MAAM,MAAM;AAEZ,MAAM,kBAAkB,CAAC,IAAI;AAEpC,cAAc;","names":[]}
1
+ {"version":3,"sources":["../../src/constants/index.ts"],"sourcesContent":["export const PLUGIN_NAME = \"payload-plugin-seo\";\nexport const PKG = \"@focus-reactive/payload-plugin-seo\";\n\nexport const DEFAULT_LOCALES = [\"en\"] as const;\n\nexport const MAX_KEYPHRASES = 5;\n\nexport * from \"./statuses\";\n"],"mappings":"AAAO,MAAM,cAAc;AACpB,MAAM,MAAM;AAEZ,MAAM,kBAAkB,CAAC,IAAI;AAE7B,MAAM,iBAAiB;AAE9B,cAAc;","names":[]}
@@ -1,4 +1,4 @@
1
- import type { AnalysisInput } from "./types/analysis";
1
+ import type { AnalysisInput, KeyphraseInput } from "./types/analysis";
2
2
  import type { PaperData } from "./types/paper";
3
- export declare function buildPaper(input: AnalysisInput): PaperData;
3
+ export declare function buildPaper(input: AnalysisInput, keyphrase?: KeyphraseInput): PaperData;
4
4
  //# sourceMappingURL=buildPaper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildPaper.d.ts","sourceRoot":"","sources":["../../src/engine/buildPaper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAU1D"}
1
+ {"version":3,"file":"buildPaper.d.ts","sourceRoot":"","sources":["../../src/engine/buildPaper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,cAAc,GAAG,SAAS,CActF"}
@@ -1,7 +1,9 @@
1
1
  import { Paper } from "yoastseo";
2
- function buildPaper(input) {
2
+ function buildPaper(input, keyphrase) {
3
+ const focus = keyphrase ?? input.keyphrases[0] ?? { text: input.keyphrase, synonyms: [] };
3
4
  return new Paper(input.contentHtml, {
4
- keyword: input.keyphrase,
5
+ keyword: focus.text,
6
+ synonyms: focus.synonyms.join(", "),
5
7
  title: input.title,
6
8
  titleWidth: 0,
7
9
  description: input.description,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/engine/buildPaper.ts"],"sourcesContent":["import { Paper } from \"yoastseo\";\nimport type { AnalysisInput } from \"./types/analysis\";\nimport type { PaperData } from \"./types/paper\";\n\nexport function buildPaper(input: AnalysisInput): PaperData {\n return new Paper(input.contentHtml, {\n keyword: input.keyphrase,\n title: input.title,\n titleWidth: 0,\n description: input.description,\n slug: input.slug,\n permalink: input.site.baseUrl ? `${input.site.baseUrl}/${input.slug}` : input.slug,\n locale: input.locale,\n });\n}\n"],"mappings":"AAAA,SAAS,aAAa;AAIf,SAAS,WAAW,OAAiC;AAC1D,SAAO,IAAI,MAAM,MAAM,aAAa;AAAA,IAClC,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,IACb,YAAY;AAAA,IACZ,aAAa,MAAM;AAAA,IACnB,MAAM,MAAM;AAAA,IACZ,WAAW,MAAM,KAAK,UAAU,GAAG,MAAM,KAAK,OAAO,IAAI,MAAM,IAAI,KAAK,MAAM;AAAA,IAC9E,QAAQ,MAAM;AAAA,EAChB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../src/engine/buildPaper.ts"],"sourcesContent":["import { Paper } from \"yoastseo\";\nimport type { AnalysisInput, KeyphraseInput } from \"./types/analysis\";\nimport type { PaperData } from \"./types/paper\";\n\nexport function buildPaper(input: AnalysisInput, keyphrase?: KeyphraseInput): PaperData {\n const focus: KeyphraseInput = keyphrase ??\n input.keyphrases[0] ?? { text: input.keyphrase, synonyms: [] };\n\n return new Paper(input.contentHtml, {\n keyword: focus.text,\n synonyms: focus.synonyms.join(\", \"),\n title: input.title,\n titleWidth: 0,\n description: input.description,\n slug: input.slug,\n permalink: input.site.baseUrl ? `${input.site.baseUrl}/${input.slug}` : input.slug,\n locale: input.locale,\n });\n}\n"],"mappings":"AAAA,SAAS,aAAa;AAIf,SAAS,WAAW,OAAsB,WAAuC;AACtF,QAAM,QAAwB,aAC5B,MAAM,WAAW,CAAC,KAAK,EAAE,MAAM,MAAM,WAAW,UAAU,CAAC,EAAE;AAE/D,SAAO,IAAI,MAAM,MAAM,aAAa;AAAA,IAClC,SAAS,MAAM;AAAA,IACf,UAAU,MAAM,SAAS,KAAK,IAAI;AAAA,IAClC,OAAO,MAAM;AAAA,IACb,YAAY;AAAA,IACZ,aAAa,MAAM;AAAA,IACnB,MAAM,MAAM;AAAA,IACZ,WAAW,MAAM,KAAK,UAAU,GAAG,MAAM,KAAK,OAAO,IAAI,MAAM,IAAI,KAAK,MAAM;AAAA,IAC9E,QAAQ,MAAM;AAAA,EAChB,CAAC;AACH;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/runAnalysis/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAOvE,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAehE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/runAnalysis/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAA0B,MAAM,mBAAmB,CAAC;AAQ/F,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAyBhE"}
@@ -1,6 +1,7 @@
1
1
  import { buildPaper } from "../buildPaper";
2
2
  import { deriveInclusive } from "./services/derive-inclusive";
3
3
  import { deriveReadability } from "./services/derive-readability";
4
+ import { deriveRelatedKeyphrase } from "./services/derive-related";
4
5
  import { deriveSeo } from "./services/derive-seo";
5
6
  import { deriveSerp } from "./services/derive-serp";
6
7
  import { deriveVitals } from "./services/derive-vitals";
@@ -8,9 +9,15 @@ function runAnalysis(input) {
8
9
  const paper = buildPaper(input);
9
10
  const { keyphrase } = input;
10
11
  const seo = deriveSeo(paper, keyphrase);
12
+ const relatedKeyphrases = input.keyphrases.slice(1).filter((kp) => kp.text.trim().length > 0).map((kp) => ({
13
+ text: kp.text,
14
+ result: deriveRelatedKeyphrase(buildPaper(input, kp), kp.text)
15
+ }));
11
16
  return {
12
17
  overall: seo.overall,
18
+ keyphraseText: keyphrase,
13
19
  keyphrase: seo.keyphrase,
20
+ relatedKeyphrases,
14
21
  onPage: seo.onPage,
15
22
  readability: deriveReadability(paper, keyphrase),
16
23
  inclusive: deriveInclusive(paper),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/engine/runAnalysis/index.ts"],"sourcesContent":["import { buildPaper } from \"../buildPaper\";\nimport type { AnalysisInput, AnalysisResult } from \"../types/analysis\";\nimport { deriveInclusive } from \"./services/derive-inclusive\";\nimport { deriveReadability } from \"./services/derive-readability\";\nimport { deriveSeo } from \"./services/derive-seo\";\nimport { deriveSerp } from \"./services/derive-serp\";\nimport { deriveVitals } from \"./services/derive-vitals\";\n\nexport function runAnalysis(input: AnalysisInput): AnalysisResult {\n const paper = buildPaper(input);\n const { keyphrase } = input;\n\n const seo = deriveSeo(paper, keyphrase);\n\n return {\n overall: seo.overall,\n keyphrase: seo.keyphrase,\n onPage: seo.onPage,\n readability: deriveReadability(paper, keyphrase),\n inclusive: deriveInclusive(paper),\n vitals: deriveVitals(paper, keyphrase),\n serp: deriveSerp(input),\n };\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAE3B,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAEtB,SAAS,YAAY,OAAsC;AAChE,QAAM,QAAQ,WAAW,KAAK;AAC9B,QAAM,EAAE,UAAU,IAAI;AAEtB,QAAM,MAAM,UAAU,OAAO,SAAS;AAEtC,SAAO;AAAA,IACL,SAAS,IAAI;AAAA,IACb,WAAW,IAAI;AAAA,IACf,QAAQ,IAAI;AAAA,IACZ,aAAa,kBAAkB,OAAO,SAAS;AAAA,IAC/C,WAAW,gBAAgB,KAAK;AAAA,IAChC,QAAQ,aAAa,OAAO,SAAS;AAAA,IACrC,MAAM,WAAW,KAAK;AAAA,EACxB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/engine/runAnalysis/index.ts"],"sourcesContent":["import { buildPaper } from \"../buildPaper\";\nimport type { AnalysisInput, AnalysisResult, RelatedKeyphraseResult } from \"../types/analysis\";\nimport { deriveInclusive } from \"./services/derive-inclusive\";\nimport { deriveReadability } from \"./services/derive-readability\";\nimport { deriveRelatedKeyphrase } from \"./services/derive-related\";\nimport { deriveSeo } from \"./services/derive-seo\";\nimport { deriveSerp } from \"./services/derive-serp\";\nimport { deriveVitals } from \"./services/derive-vitals\";\n\nexport function runAnalysis(input: AnalysisInput): AnalysisResult {\n const paper = buildPaper(input);\n const { keyphrase } = input;\n\n const seo = deriveSeo(paper, keyphrase);\n\n const relatedKeyphrases: RelatedKeyphraseResult[] = input.keyphrases\n .slice(1)\n .filter((kp) => kp.text.trim().length > 0)\n .map((kp) => ({\n text: kp.text,\n result: deriveRelatedKeyphrase(buildPaper(input, kp), kp.text),\n }));\n\n return {\n overall: seo.overall,\n keyphraseText: keyphrase,\n keyphrase: seo.keyphrase,\n relatedKeyphrases,\n onPage: seo.onPage,\n readability: deriveReadability(paper, keyphrase),\n inclusive: deriveInclusive(paper),\n vitals: deriveVitals(paper, keyphrase),\n serp: deriveSerp(input),\n };\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAE3B,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAEtB,SAAS,YAAY,OAAsC;AAChE,QAAM,QAAQ,WAAW,KAAK;AAC9B,QAAM,EAAE,UAAU,IAAI;AAEtB,QAAM,MAAM,UAAU,OAAO,SAAS;AAEtC,QAAM,oBAA8C,MAAM,WACvD,MAAM,CAAC,EACP,OAAO,CAAC,OAAO,GAAG,KAAK,KAAK,EAAE,SAAS,CAAC,EACxC,IAAI,CAAC,QAAQ;AAAA,IACZ,MAAM,GAAG;AAAA,IACT,QAAQ,uBAAuB,WAAW,OAAO,EAAE,GAAG,GAAG,IAAI;AAAA,EAC/D,EAAE;AAEJ,SAAO;AAAA,IACL,SAAS,IAAI;AAAA,IACb,eAAe;AAAA,IACf,WAAW,IAAI;AAAA,IACf;AAAA,IACA,QAAQ,IAAI;AAAA,IACZ,aAAa,kBAAkB,OAAO,SAAS;AAAA,IAC/C,WAAW,gBAAgB,KAAK;AAAA,IAChC,QAAQ,aAAa,OAAO,SAAS;AAAA,IACrC,MAAM,WAAW,KAAK;AAAA,EACxB;AACF;","names":[]}
@@ -0,0 +1,4 @@
1
+ import type { CategoryResult } from "../../types/analysis";
2
+ import type { PaperData } from "../../types/paper";
3
+ export declare function deriveRelatedKeyphrase(paper: PaperData, keyphrase: string): CategoryResult;
4
+ //# sourceMappingURL=derive-related.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-related.d.ts","sourceRoot":"","sources":["../../../../src/engine/runAnalysis/services/derive-related.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,mBAAmB,CAAC;AAI9D,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,CAO1F"}
@@ -0,0 +1,16 @@
1
+ import RelatedKeywordAssessor from "yoastseo/build/scoring/assessors/relatedKeywordAssessor";
2
+ import { runAssessor } from "../../assessorAdapter";
3
+ import { makeResearcher } from "../../researcherAdapter";
4
+ import { enrich } from "../utils/enrich";
5
+ import { toCategory } from "../utils/toCategory";
6
+ function deriveRelatedKeyphrase(paper, keyphrase) {
7
+ const ctx = { keyphrase };
8
+ const researcher = makeResearcher(paper);
9
+ const assessor = new RelatedKeywordAssessor(makeResearcher(paper));
10
+ const checks = enrich(runAssessor(assessor, ctx, paper), paper, researcher);
11
+ return toCategory(checks);
12
+ }
13
+ export {
14
+ deriveRelatedKeyphrase
15
+ };
16
+ //# sourceMappingURL=derive-related.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/engine/runAnalysis/services/derive-related.ts"],"sourcesContent":["import RelatedKeywordAssessor from \"yoastseo/build/scoring/assessors/relatedKeywordAssessor\";\nimport { runAssessor } from \"../../assessorAdapter\";\nimport { makeResearcher } from \"../../researcherAdapter\";\nimport type { CategoryResult } from \"../../types/analysis\";\nimport type { PaperData, PaperLike } from \"../../types/paper\";\nimport { enrich } from \"../utils/enrich\";\nimport { toCategory } from \"../utils/toCategory\";\n\nexport function deriveRelatedKeyphrase(paper: PaperData, keyphrase: string): CategoryResult {\n const ctx = { keyphrase };\n const researcher = makeResearcher(paper);\n const assessor = new RelatedKeywordAssessor(makeResearcher(paper));\n const checks = enrich(runAssessor(assessor, ctx, paper), paper as PaperLike, researcher);\n\n return toCategory(checks);\n}\n"],"mappings":"AAAA,OAAO,4BAA4B;AACnC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAG/B,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAEpB,SAAS,uBAAuB,OAAkB,WAAmC;AAC1F,QAAM,MAAM,EAAE,UAAU;AACxB,QAAM,aAAa,eAAe,KAAK;AACvC,QAAM,WAAW,IAAI,uBAAuB,eAAe,KAAK,CAAC;AACjE,QAAM,SAAS,OAAO,YAAY,UAAU,KAAK,KAAK,GAAG,OAAoB,UAAU;AAEvF,SAAO,WAAW,MAAM;AAC1B;","names":[]}
@@ -0,0 +1,8 @@
1
+ import type { HeadingDocIssue, HeadingNodeIssue } from "../../../types/analysis";
2
+ import type { FlatHeading } from "./heading-tree";
3
+ export interface HeadingIssues {
4
+ docIssues: HeadingDocIssue[];
5
+ nodeIssues: (HeadingNodeIssue | undefined)[];
6
+ }
7
+ export declare function deriveHeadingIssues(flat: FlatHeading[]): HeadingIssues;
8
+ //# sourceMappingURL=heading-issues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heading-issues.d.ts","sourceRoot":"","sources":["../../../../../src/engine/runAnalysis/services/derive-vitals/heading-issues.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC/F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,UAAU,EAAE,CAAC,gBAAgB,GAAG,SAAS,CAAC,EAAE,CAAC;CAC9C;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,aAAa,CAKtE"}
@@ -0,0 +1,37 @@
1
+ function deriveHeadingIssues(flat) {
2
+ return {
3
+ docIssues: deriveDocIssues(flat),
4
+ nodeIssues: flat.map((_heading, index) => deriveNodeIssue(flat, index))
5
+ };
6
+ }
7
+ function deriveDocIssues(flat) {
8
+ const h1Count = flat.filter((heading) => heading.level === 1).length;
9
+ if (h1Count === 0)
10
+ return [{ type: "missing-h1" }];
11
+ if (h1Count > 1)
12
+ return [{ type: "multiple-h1", count: h1Count }];
13
+ return [];
14
+ }
15
+ function deriveNodeIssue(flat, index) {
16
+ const current = flat[index];
17
+ const previous = flat[index - 1];
18
+ if (!current || !previous)
19
+ return void 0;
20
+ if (current.level - previous.level <= 1)
21
+ return void 0;
22
+ return {
23
+ type: "skipped-level",
24
+ skipped: levelsBetween(previous.level, current.level)
25
+ };
26
+ }
27
+ function levelsBetween(from, to) {
28
+ const skipped = [];
29
+ for (let level = from + 1; level < to; level++) {
30
+ skipped.push(level);
31
+ }
32
+ return skipped;
33
+ }
34
+ export {
35
+ deriveHeadingIssues
36
+ };
37
+ //# sourceMappingURL=heading-issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/engine/runAnalysis/services/derive-vitals/heading-issues.ts"],"sourcesContent":["import type { HeadingDocIssue, HeadingLevel, HeadingNodeIssue } from \"../../../types/analysis\";\nimport type { FlatHeading } from \"./heading-tree\";\n\nexport interface HeadingIssues {\n docIssues: HeadingDocIssue[];\n nodeIssues: (HeadingNodeIssue | undefined)[];\n}\n\nexport function deriveHeadingIssues(flat: FlatHeading[]): HeadingIssues {\n return {\n docIssues: deriveDocIssues(flat),\n nodeIssues: flat.map((_heading, index) => deriveNodeIssue(flat, index)),\n };\n}\n\nfunction deriveDocIssues(flat: FlatHeading[]): HeadingDocIssue[] {\n const h1Count = flat.filter((heading) => heading.level === 1).length;\n\n if (h1Count === 0) return [{ type: \"missing-h1\" }];\n if (h1Count > 1) return [{ type: \"multiple-h1\", count: h1Count }];\n\n return [];\n}\n\nfunction deriveNodeIssue(flat: FlatHeading[], index: number): HeadingNodeIssue | undefined {\n const current = flat[index];\n const previous = flat[index - 1];\n if (!current || !previous) return undefined;\n\n if (current.level - previous.level <= 1) return undefined;\n\n return {\n type: \"skipped-level\",\n skipped: levelsBetween(previous.level, current.level),\n };\n}\n\nfunction levelsBetween(from: HeadingLevel, to: HeadingLevel): HeadingLevel[] {\n const skipped: HeadingLevel[] = [];\n\n for (let level = from + 1; level < to; level++) {\n skipped.push(level as HeadingLevel);\n }\n\n return skipped;\n}\n"],"mappings":"AAQO,SAAS,oBAAoB,MAAoC;AACtE,SAAO;AAAA,IACL,WAAW,gBAAgB,IAAI;AAAA,IAC/B,YAAY,KAAK,IAAI,CAAC,UAAU,UAAU,gBAAgB,MAAM,KAAK,CAAC;AAAA,EACxE;AACF;AAEA,SAAS,gBAAgB,MAAwC;AAC/D,QAAM,UAAU,KAAK,OAAO,CAAC,YAAY,QAAQ,UAAU,CAAC,EAAE;AAE9D,MAAI,YAAY;AAAG,WAAO,CAAC,EAAE,MAAM,aAAa,CAAC;AACjD,MAAI,UAAU;AAAG,WAAO,CAAC,EAAE,MAAM,eAAe,OAAO,QAAQ,CAAC;AAEhE,SAAO,CAAC;AACV;AAEA,SAAS,gBAAgB,MAAqB,OAA6C;AACzF,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,WAAW,KAAK,QAAQ,CAAC;AAC/B,MAAI,CAAC,WAAW,CAAC;AAAU,WAAO;AAElC,MAAI,QAAQ,QAAQ,SAAS,SAAS;AAAG,WAAO;AAEhD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,cAAc,SAAS,OAAO,QAAQ,KAAK;AAAA,EACtD;AACF;AAEA,SAAS,cAAc,MAAoB,IAAkC;AAC3E,QAAM,UAA0B,CAAC;AAEjC,WAAS,QAAQ,OAAO,GAAG,QAAQ,IAAI,SAAS;AAC9C,YAAQ,KAAK,KAAqB;AAAA,EACpC;AAEA,SAAO;AACT;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"heading-tree.d.ts","sourceRoot":"","sources":["../../../../../src/engine/runAnalysis/services/derive-vitals/heading-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAGZ,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAWD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,gBAAgB,CAkCtE"}
1
+ {"version":3,"file":"heading-tree.d.ts","sourceRoot":"","sources":["../../../../../src/engine/runAnalysis/services/derive-vitals/heading-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAGZ,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAGjC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAWD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,gBAAgB,CAqCtE"}
@@ -1,3 +1,4 @@
1
+ import { deriveHeadingIssues } from "./heading-issues";
1
2
  const LEVELS = [1, 2, 3, 4, 5, 6];
2
3
  function countLevels(flat) {
3
4
  return LEVELS.map((level) => ({
@@ -6,9 +7,10 @@ function countLevels(flat) {
6
7
  }));
7
8
  }
8
9
  function buildHeadingTree(flat) {
10
+ const { docIssues, nodeIssues } = deriveHeadingIssues(flat);
9
11
  const roots = [];
10
12
  const stack = [];
11
- for (const heading of flat) {
13
+ for (const [index, heading] of flat.entries()) {
12
14
  let top = stack.at(-1);
13
15
  while (top && top.level >= heading.level) {
14
16
  stack.pop();
@@ -20,7 +22,8 @@ function buildHeadingTree(flat) {
20
22
  id,
21
23
  level: heading.level,
22
24
  text: heading.text,
23
- children: []
25
+ children: [],
26
+ issue: nodeIssues[index]
24
27
  };
25
28
  if (parent) {
26
29
  parent.children.push(node);
@@ -32,7 +35,8 @@ function buildHeadingTree(flat) {
32
35
  return {
33
36
  total: flat.length,
34
37
  levels: countLevels(flat),
35
- tree: roots
38
+ tree: roots,
39
+ issues: docIssues
36
40
  };
37
41
  }
38
42
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/engine/runAnalysis/services/derive-vitals/heading-tree.ts"],"sourcesContent":["import type {\n HeadingLevel,\n HeadingLevelCount,\n HeadingNode,\n HeadingStructure,\n} from \"../../../types/analysis\";\n\nexport interface FlatHeading {\n level: HeadingLevel;\n text: string;\n}\n\nconst LEVELS: HeadingLevel[] = [1, 2, 3, 4, 5, 6];\n\nfunction countLevels(flat: FlatHeading[]): HeadingLevelCount[] {\n return LEVELS.map((level) => ({\n level,\n count: flat.filter((h) => h.level === level).length,\n }));\n}\n\nexport function buildHeadingTree(flat: FlatHeading[]): HeadingStructure {\n const roots: HeadingNode[] = [];\n const stack: HeadingNode[] = [];\n\n for (const heading of flat) {\n let top = stack.at(-1);\n\n while (top && top.level >= heading.level) {\n stack.pop();\n top = stack.at(-1);\n }\n\n const parent = top;\n const id = parent ? `${parent.id}.${parent.children.length}` : `${roots.length}`;\n\n const node: HeadingNode = {\n id,\n level: heading.level,\n text: heading.text,\n children: [],\n };\n if (parent) {\n parent.children.push(node);\n } else {\n roots.push(node);\n }\n stack.push(node);\n }\n\n return {\n total: flat.length,\n levels: countLevels(flat),\n tree: roots,\n };\n}\n"],"mappings":"AAYA,MAAM,SAAyB,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEhD,SAAS,YAAY,MAA0C;AAC7D,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B;AAAA,IACA,OAAO,KAAK,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,EAAE;AAAA,EAC/C,EAAE;AACJ;AAEO,SAAS,iBAAiB,MAAuC;AACtE,QAAM,QAAuB,CAAC;AAC9B,QAAM,QAAuB,CAAC;AAE9B,aAAW,WAAW,MAAM;AAC1B,QAAI,MAAM,MAAM,GAAG,EAAE;AAErB,WAAO,OAAO,IAAI,SAAS,QAAQ,OAAO;AACxC,YAAM,IAAI;AACV,YAAM,MAAM,GAAG,EAAE;AAAA,IACnB;AAEA,UAAM,SAAS;AACf,UAAM,KAAK,SAAS,GAAG,OAAO,EAAE,IAAI,OAAO,SAAS,MAAM,KAAK,GAAG,MAAM,MAAM;AAE9E,UAAM,OAAoB;AAAA,MACxB;AAAA,MACA,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ;AAAA,MACd,UAAU,CAAC;AAAA,IACb;AACA,QAAI,QAAQ;AACV,aAAO,SAAS,KAAK,IAAI;AAAA,IAC3B,OAAO;AACL,YAAM,KAAK,IAAI;AAAA,IACjB;AACA,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,QAAQ,YAAY,IAAI;AAAA,IACxB,MAAM;AAAA,EACR;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/engine/runAnalysis/services/derive-vitals/heading-tree.ts"],"sourcesContent":["import type {\n HeadingLevel,\n HeadingLevelCount,\n HeadingNode,\n HeadingStructure,\n} from \"../../../types/analysis\";\nimport { deriveHeadingIssues } from \"./heading-issues\";\n\nexport interface FlatHeading {\n level: HeadingLevel;\n text: string;\n}\n\nconst LEVELS: HeadingLevel[] = [1, 2, 3, 4, 5, 6];\n\nfunction countLevels(flat: FlatHeading[]): HeadingLevelCount[] {\n return LEVELS.map((level) => ({\n level,\n count: flat.filter((h) => h.level === level).length,\n }));\n}\n\nexport function buildHeadingTree(flat: FlatHeading[]): HeadingStructure {\n const { docIssues, nodeIssues } = deriveHeadingIssues(flat);\n const roots: HeadingNode[] = [];\n const stack: HeadingNode[] = [];\n\n for (const [index, heading] of flat.entries()) {\n let top = stack.at(-1);\n\n while (top && top.level >= heading.level) {\n stack.pop();\n top = stack.at(-1);\n }\n\n const parent = top;\n const id = parent ? `${parent.id}.${parent.children.length}` : `${roots.length}`;\n\n const node: HeadingNode = {\n id,\n level: heading.level,\n text: heading.text,\n children: [],\n issue: nodeIssues[index],\n };\n if (parent) {\n parent.children.push(node);\n } else {\n roots.push(node);\n }\n stack.push(node);\n }\n\n return {\n total: flat.length,\n levels: countLevels(flat),\n tree: roots,\n issues: docIssues,\n };\n}\n"],"mappings":"AAMA,SAAS,2BAA2B;AAOpC,MAAM,SAAyB,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEhD,SAAS,YAAY,MAA0C;AAC7D,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B;AAAA,IACA,OAAO,KAAK,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,EAAE;AAAA,EAC/C,EAAE;AACJ;AAEO,SAAS,iBAAiB,MAAuC;AACtE,QAAM,EAAE,WAAW,WAAW,IAAI,oBAAoB,IAAI;AAC1D,QAAM,QAAuB,CAAC;AAC9B,QAAM,QAAuB,CAAC;AAE9B,aAAW,CAAC,OAAO,OAAO,KAAK,KAAK,QAAQ,GAAG;AAC7C,QAAI,MAAM,MAAM,GAAG,EAAE;AAErB,WAAO,OAAO,IAAI,SAAS,QAAQ,OAAO;AACxC,YAAM,IAAI;AACV,YAAM,MAAM,GAAG,EAAE;AAAA,IACnB;AAEA,UAAM,SAAS;AACf,UAAM,KAAK,SAAS,GAAG,OAAO,EAAE,IAAI,OAAO,SAAS,MAAM,KAAK,GAAG,MAAM,MAAM;AAE9E,UAAM,OAAoB;AAAA,MACxB;AAAA,MACA,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ;AAAA,MACd,UAAU,CAAC;AAAA,MACX,OAAO,WAAW,KAAK;AAAA,IACzB;AACA,QAAI,QAAQ;AACV,aAAO,SAAS,KAAK,IAAI;AAAA,IAC3B,OAAO;AACL,YAAM,KAAK,IAAI;AAAA,IACjB;AACA,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,QAAQ,YAAY,IAAI;AAAA,IACxB,MAAM;AAAA,IACN,QAAQ;AAAA,EACV;AACF;","names":[]}
@@ -24,11 +24,22 @@ export interface ProminentWord {
24
24
  isKeyphrase: boolean;
25
25
  }
26
26
  export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
27
+ export type HeadingDocIssue = {
28
+ type: "missing-h1";
29
+ } | {
30
+ type: "multiple-h1";
31
+ count: number;
32
+ };
33
+ export interface HeadingNodeIssue {
34
+ type: "skipped-level";
35
+ skipped: HeadingLevel[];
36
+ }
27
37
  export interface HeadingNode {
28
38
  id: string;
29
39
  level: HeadingLevel;
30
40
  text: string;
31
41
  children: HeadingNode[];
42
+ issue?: HeadingNodeIssue;
32
43
  }
33
44
  export interface HeadingLevelCount {
34
45
  level: HeadingLevel;
@@ -38,6 +49,7 @@ export interface HeadingStructure {
38
49
  total: number;
39
50
  levels: HeadingLevelCount[];
40
51
  tree: HeadingNode[];
52
+ issues: HeadingDocIssue[];
41
53
  }
42
54
  export interface VitalsResult {
43
55
  words: number;
@@ -60,12 +72,22 @@ export interface CategoryResult {
60
72
  status: Status;
61
73
  checks: CheckResult[];
62
74
  }
75
+ export interface KeyphraseInput {
76
+ text: string;
77
+ synonyms: string[];
78
+ }
79
+ export interface RelatedKeyphraseResult {
80
+ text: string;
81
+ result: CategoryResult;
82
+ }
63
83
  export interface AnalysisResult {
64
84
  overall: {
65
85
  seoScore: number;
66
86
  status: Status;
67
87
  };
88
+ keyphraseText: string;
68
89
  keyphrase: CategoryResult;
90
+ relatedKeyphrases: RelatedKeyphraseResult[];
69
91
  onPage: CategoryResult;
70
92
  readability: CategoryResult;
71
93
  inclusive: {
@@ -83,6 +105,7 @@ export interface AnalysisInput {
83
105
  description: string;
84
106
  contentHtml: string;
85
107
  keyphrase: string;
108
+ keyphrases: KeyphraseInput[];
86
109
  locale: string;
87
110
  site: {
88
111
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../../../src/engine/types/analysis.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../../../src/engine/types/analysis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from "react";
2
+ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
3
+ variant?: "primary" | "error";
4
+ icon?: ReactNode;
5
+ children: ReactNode;
6
+ }
7
+ export declare function Button({ variant, icon, children, className, type, ...props }: ButtonProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=Button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/ui/Button.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAiB7D,UAAU,WAAY,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IACnE,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,IAAe,EACf,GAAG,KAAK,EACT,EAAE,WAAW,2CAOb"}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsxs } from "react/jsx-runtime";
3
+ import { cva } from "class-variance-authority";
4
+ const buttonVariants = cva(
5
+ "inline-flex items-center justify-center gap-[6px] rounded-rs px-[14px] py-[8px] text-[12px] font-semibold [&_svg]:size-[14px]",
6
+ {
7
+ variants: {
8
+ variant: {
9
+ primary: "text-neutral-0 bg-neutral-1000 hover:bg-black",
10
+ error: "text-seo-bad bg-seo-bad-100 hover:bg-seo-200"
11
+ }
12
+ },
13
+ defaultVariants: {
14
+ variant: "primary"
15
+ }
16
+ }
17
+ );
18
+ function Button({
19
+ variant,
20
+ icon,
21
+ children,
22
+ className,
23
+ type = "button",
24
+ ...props
25
+ }) {
26
+ return /* @__PURE__ */ jsxs("button", { className: buttonVariants({ variant, className }), type, ...props, children: [
27
+ icon,
28
+ children
29
+ ] });
30
+ }
31
+ export {
32
+ Button
33
+ };
34
+ //# sourceMappingURL=Button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/Button.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ButtonHTMLAttributes, ReactNode } from \"react\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-[6px] rounded-rs px-[14px] py-[8px] text-[12px] font-semibold [&_svg]:size-[14px]\",\n {\n variants: {\n variant: {\n primary: \"text-neutral-0 bg-neutral-1000 hover:bg-black\",\n error: \"text-seo-bad bg-seo-bad-100 hover:bg-seo-200\",\n },\n },\n defaultVariants: {\n variant: \"primary\",\n },\n }\n);\n\ninterface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: \"primary\" | \"error\";\n icon?: ReactNode;\n children: ReactNode;\n}\n\nexport function Button({\n variant,\n icon,\n children,\n className,\n type = \"button\",\n ...props\n}: ButtonProps) {\n return (\n <button className={buttonVariants({ variant, className })} type={type} {...props}>\n {icon}\n {children}\n </button>\n );\n}\n"],"mappings":";AAmCI;AAjCJ,SAAS,WAAW;AAGpB,MAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAQO,SAAS,OAAO;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,GAAG;AACL,GAAgB;AACd,SACE,qBAAC,YAAO,WAAW,eAAe,EAAE,SAAS,UAAU,CAAC,GAAG,MAAa,GAAG,OACxE;AAAA;AAAA,IACA;AAAA,KACH;AAEJ;","names":[]}
@@ -1,8 +1,21 @@
1
+ import type { VariantProps } from "class-variance-authority";
1
2
  import type { Status } from "../engine/types/analysis";
2
- interface ScoreRingProps {
3
- score: number;
3
+ declare const ringVar: (props?: ({
4
+ size?: "small" | "medium" | null | undefined;
5
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
+ type RingSize = NonNullable<VariantProps<typeof ringVar>["size"]>;
7
+ type ScoreRingProps = {
8
+ size?: RingSize;
9
+ } & ({
4
10
  status: Status;
5
- }
6
- export declare function ScoreRing({ score, status }: ScoreRingProps): import("react/jsx-runtime").JSX.Element;
11
+ score: number;
12
+ } | {
13
+ status: "idle";
14
+ score?: never;
15
+ } | {
16
+ status: "loading";
17
+ score?: never;
18
+ });
19
+ export declare function ScoreRing({ size, status, score }: ScoreRingProps): import("react/jsx-runtime").JSX.Element;
7
20
  export {};
8
21
  //# sourceMappingURL=ScoreRing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ScoreRing.d.ts","sourceRoot":"","sources":["../../src/ui/ScoreRing.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAIvD,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,cAAc,2CAmB1D"}
1
+ {"version":3,"file":"ScoreRing.d.ts","sourceRoot":"","sources":["../../src/ui/ScoreRing.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAIvD,QAAA,MAAM,OAAO;;8EAQX,CAAC;AAsBH,KAAK,QAAQ,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAElE,KAAK,cAAc,GAAG;IAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,GAAG,CACxC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GACjC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CACvC,CAAC;AAEF,wBAAgB,SAAS,CAAC,EAAE,IAAe,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,cAAc,2CAwB3E"}
@@ -1,26 +1,50 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
+ import { cva } from "class-variance-authority";
4
+ import { Loader2 } from "lucide-react";
3
5
  import { cn } from "../utils/style";
4
6
  import { statusVar } from "../components/SeoDrawer/variants";
5
- function ScoreRing({ score, status }) {
7
+ const ringVar = cva("rounded-full flex-none grid place-items-center", {
8
+ variants: {
9
+ size: {
10
+ small: "w-[36px] h-[36px]",
11
+ medium: "w-[60px] h-[60px]"
12
+ }
13
+ },
14
+ defaultVariants: { size: "medium" }
15
+ });
16
+ const innerVar = cva("rounded-full bg-neutral-0 grid place-items-center font-bold", {
17
+ variants: {
18
+ size: {
19
+ small: "w-[28px] h-[28px] text-[11px]",
20
+ medium: "w-[48px] h-[48px] text-[18px]"
21
+ }
22
+ },
23
+ defaultVariants: { size: "medium" }
24
+ });
25
+ const loaderVar = cva("animate-spin text-neutral-500", {
26
+ variants: {
27
+ size: {
28
+ small: "w-[16px] h-[16px]",
29
+ medium: "w-[26px] h-[26px]"
30
+ }
31
+ },
32
+ defaultVariants: { size: "medium" }
33
+ });
34
+ function ScoreRing({ size = "medium", status, score }) {
35
+ if (status === "loading") {
36
+ return /* @__PURE__ */ jsx("span", { className: cn(ringVar({ size }), "bg-neutral-100"), children: /* @__PURE__ */ jsx(Loader2, { "aria-hidden": "true", className: loaderVar({ size }) }) });
37
+ }
38
+ const isIdle = status === "idle";
39
+ const fill = isIdle ? 100 : score;
6
40
  return /* @__PURE__ */ jsx(
7
41
  "div",
8
42
  {
9
- className: cn(
10
- "w-[60px] h-[60px] rounded-full flex-none grid place-items-center",
11
- statusVar({ status })
12
- ),
43
+ className: cn(ringVar({ size }), statusVar({ status })),
13
44
  style: {
14
- background: `conic-gradient(var(--seo-c) ${score}%, var(--theme-elevation-150) 0)`
45
+ background: `conic-gradient(var(--seo-c) ${fill}%, var(--theme-elevation-150) 0)`
15
46
  },
16
- children: /* @__PURE__ */ jsx(
17
- "div",
18
- {
19
- className: "w-[48px] h-[48px] rounded-full bg-neutral-0 grid place-items-center font-bold text-[18px]",
20
- style: { color: "var(--seo-c)" },
21
- children: score
22
- }
23
- )
47
+ children: /* @__PURE__ */ jsx("div", { className: innerVar({ size }), style: { color: "var(--seo-c)" }, children: isIdle ? "-" : score })
24
48
  }
25
49
  );
26
50
  }