@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
@@ -10,7 +10,13 @@ import {
10
10
  serpUrlRow
11
11
  } from "./variants";
12
12
  import { truncateDescription } from "./truncate-description";
13
- function SerpPreview({ data, keyphrase, faviconUrl, mode }) {
13
+ function SerpPreview({
14
+ data,
15
+ keyphrase,
16
+ synonyms = [],
17
+ faviconUrl,
18
+ mode
19
+ }) {
14
20
  const result = /* @__PURE__ */ jsxs("div", { className: serpContainer({ mode }), children: [
15
21
  /* @__PURE__ */ jsxs("div", { className: serpUrlRow({ mode }), children: [
16
22
  /* @__PURE__ */ jsx(SerpFavicon, { faviconUrl, siteName: data.siteName }),
@@ -20,7 +26,7 @@ function SerpPreview({ data, keyphrase, faviconUrl, mode }) {
20
26
  ] })
21
27
  ] }),
22
28
  /* @__PURE__ */ jsx("div", { className: serpTitle({ mode }), children: data.title }),
23
- /* @__PURE__ */ jsx("div", { className: serpDescription({ mode }), children: highlightKeyphrase(truncateDescription(data.description), keyphrase) })
29
+ /* @__PURE__ */ jsx("div", { className: serpDescription({ mode }), children: highlightKeyphrase(truncateDescription(data.description), keyphrase, synonyms) })
24
30
  ] });
25
31
  return mode === "desktop" ? /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: result }) : result;
26
32
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/components/SeoDrawer/components/SerpPreview/index.tsx"],"sourcesContent":["import type { ReactElement } from \"react\";\nimport type { SerpResult } from \"../../../../engine/types/analysis\";\nimport { highlightKeyphrase } from \"./highlight-keyphrase\";\nimport { SerpFavicon } from \"./serp-favicon\";\nimport {\n serpContainer,\n serpDescription,\n serpHostname,\n serpSiteName,\n serpTitle,\n serpUrlRow,\n} from \"./variants\";\nimport { truncateDescription } from \"./truncate-description\";\n\nexport type SerpMode = \"mobile\" | \"desktop\";\n\ninterface SerpPreviewProps {\n data: SerpResult;\n keyphrase: string;\n faviconUrl: string;\n mode: SerpMode;\n}\n\nexport function SerpPreview({ data, keyphrase, faviconUrl, mode }: SerpPreviewProps): ReactElement {\n const result = (\n <div className={serpContainer({ mode })}>\n <div className={serpUrlRow({ mode })}>\n <SerpFavicon faviconUrl={faviconUrl} siteName={data.siteName} />\n\n <div className=\"overflow-hidden\">\n <div className={serpSiteName({ mode })}>{data.siteName}</div>\n <div className={serpHostname({ mode })}>{data.url}</div>\n </div>\n </div>\n\n <div className={serpTitle({ mode })}>{data.title}</div>\n\n <div className={serpDescription({ mode })}>\n {highlightKeyphrase(truncateDescription(data.description), keyphrase)}\n </div>\n </div>\n );\n\n return mode === \"desktop\" ? <div className=\"overflow-x-auto\">{result}</div> : result;\n}\n"],"mappings":"AA2BQ,cAEA,YAFA;AAzBR,SAAS,0BAA0B;AACnC,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAW7B,SAAS,YAAY,EAAE,MAAM,WAAW,YAAY,KAAK,GAAmC;AACjG,QAAM,SACJ,qBAAC,SAAI,WAAW,cAAc,EAAE,KAAK,CAAC,GACpC;AAAA,yBAAC,SAAI,WAAW,WAAW,EAAE,KAAK,CAAC,GACjC;AAAA,0BAAC,eAAY,YAAwB,UAAU,KAAK,UAAU;AAAA,MAE9D,qBAAC,SAAI,WAAU,mBACb;AAAA,4BAAC,SAAI,WAAW,aAAa,EAAE,KAAK,CAAC,GAAI,eAAK,UAAS;AAAA,QACvD,oBAAC,SAAI,WAAW,aAAa,EAAE,KAAK,CAAC,GAAI,eAAK,KAAI;AAAA,SACpD;AAAA,OACF;AAAA,IAEA,oBAAC,SAAI,WAAW,UAAU,EAAE,KAAK,CAAC,GAAI,eAAK,OAAM;AAAA,IAEjD,oBAAC,SAAI,WAAW,gBAAgB,EAAE,KAAK,CAAC,GACrC,6BAAmB,oBAAoB,KAAK,WAAW,GAAG,SAAS,GACtE;AAAA,KACF;AAGF,SAAO,SAAS,YAAY,oBAAC,SAAI,WAAU,mBAAmB,kBAAO,IAAS;AAChF;","names":[]}
1
+ {"version":3,"sources":["../../../../../src/components/SeoDrawer/components/SerpPreview/index.tsx"],"sourcesContent":["import type { ReactElement } from \"react\";\nimport type { SerpResult } from \"../../../../engine/types/analysis\";\nimport { highlightKeyphrase } from \"./highlight-keyphrase\";\nimport { SerpFavicon } from \"./serp-favicon\";\nimport {\n serpContainer,\n serpDescription,\n serpHostname,\n serpSiteName,\n serpTitle,\n serpUrlRow,\n} from \"./variants\";\nimport { truncateDescription } from \"./truncate-description\";\n\nexport type SerpMode = \"mobile\" | \"desktop\";\n\ninterface SerpPreviewProps {\n data: SerpResult;\n keyphrase: string;\n synonyms?: string[];\n faviconUrl: string;\n mode: SerpMode;\n}\n\nexport function SerpPreview({\n data,\n keyphrase,\n synonyms = [],\n faviconUrl,\n mode,\n}: SerpPreviewProps): ReactElement {\n const result = (\n <div className={serpContainer({ mode })}>\n <div className={serpUrlRow({ mode })}>\n <SerpFavicon faviconUrl={faviconUrl} siteName={data.siteName} />\n\n <div className=\"overflow-hidden\">\n <div className={serpSiteName({ mode })}>{data.siteName}</div>\n <div className={serpHostname({ mode })}>{data.url}</div>\n </div>\n </div>\n\n <div className={serpTitle({ mode })}>{data.title}</div>\n\n <div className={serpDescription({ mode })}>\n {highlightKeyphrase(truncateDescription(data.description), keyphrase, synonyms)}\n </div>\n </div>\n );\n\n return mode === \"desktop\" ? <div className=\"overflow-x-auto\">{result}</div> : result;\n}\n"],"mappings":"AAkCQ,cAEA,YAFA;AAhCR,SAAS,0BAA0B;AACnC,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAY7B,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,WAAW,CAAC;AAAA,EACZ;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,SACJ,qBAAC,SAAI,WAAW,cAAc,EAAE,KAAK,CAAC,GACpC;AAAA,yBAAC,SAAI,WAAW,WAAW,EAAE,KAAK,CAAC,GACjC;AAAA,0BAAC,eAAY,YAAwB,UAAU,KAAK,UAAU;AAAA,MAE9D,qBAAC,SAAI,WAAU,mBACb;AAAA,4BAAC,SAAI,WAAW,aAAa,EAAE,KAAK,CAAC,GAAI,eAAK,UAAS;AAAA,QACvD,oBAAC,SAAI,WAAW,aAAa,EAAE,KAAK,CAAC,GAAI,eAAK,KAAI;AAAA,SACpD;AAAA,OACF;AAAA,IAEA,oBAAC,SAAI,WAAW,UAAU,EAAE,KAAK,CAAC,GAAI,eAAK,OAAM;AAAA,IAEjD,oBAAC,SAAI,WAAW,gBAAgB,EAAE,KAAK,CAAC,GACrC,6BAAmB,oBAAoB,KAAK,WAAW,GAAG,WAAW,QAAQ,GAChF;AAAA,KACF;AAGF,SAAO,SAAS,YAAY,oBAAC,SAAI,WAAU,mBAAmB,kBAAO,IAAS;AAChF;","names":[]}
@@ -1,19 +1,19 @@
1
1
  export declare const serpContainer: (props?: ({
2
- mode?: "desktop" | "mobile" | null | undefined;
2
+ mode?: "mobile" | "desktop" | null | undefined;
3
3
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
4
4
  export declare const serpUrlRow: (props?: ({
5
- mode?: "desktop" | "mobile" | null | undefined;
5
+ mode?: "mobile" | "desktop" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  export declare const serpSiteName: (props?: ({
8
- mode?: "desktop" | "mobile" | null | undefined;
8
+ mode?: "mobile" | "desktop" | null | undefined;
9
9
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
10
10
  export declare const serpHostname: (props?: ({
11
- mode?: "desktop" | "mobile" | null | undefined;
11
+ mode?: "mobile" | "desktop" | null | undefined;
12
12
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
13
13
  export declare const serpTitle: (props?: ({
14
- mode?: "desktop" | "mobile" | null | undefined;
14
+ mode?: "mobile" | "desktop" | null | undefined;
15
15
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
16
16
  export declare const serpDescription: (props?: ({
17
- mode?: "desktop" | "mobile" | null | undefined;
17
+ mode?: "mobile" | "desktop" | null | undefined;
18
18
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
19
19
  //# sourceMappingURL=variants.d.ts.map
@@ -0,0 +1,6 @@
1
+ interface TabWrapperProps {
2
+ children: React.ReactNode;
3
+ }
4
+ export declare function TabWrapper({ children }: TabWrapperProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
6
+ //# sourceMappingURL=TabWrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabWrapper.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/components/TabWrapper.tsx"],"names":[],"mappings":"AAAA,UAAU,eAAe;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAEvD"}
@@ -0,0 +1,8 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ function TabWrapper({ children }) {
3
+ return /* @__PURE__ */ jsx("div", { className: "py-[18px]", children });
4
+ }
5
+ export {
6
+ TabWrapper
7
+ };
8
+ //# sourceMappingURL=TabWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/SeoDrawer/components/TabWrapper.tsx"],"sourcesContent":["interface TabWrapperProps {\n children: React.ReactNode;\n}\n\nexport function TabWrapper({ children }: TabWrapperProps) {\n return <div className=\"py-[18px]\">{children}</div>;\n}\n"],"mappings":"AAKS;AADF,SAAS,WAAW,EAAE,SAAS,GAAoB;AACxD,SAAO,oBAAC,SAAI,WAAU,aAAa,UAAS;AAC9C;","names":[]}
@@ -1,13 +1,20 @@
1
1
  import type { AnalysisResult } from "../../engine/types/analysis";
2
+ import type { KeyphraseEntry } from "./keyphraseState";
2
3
  import "../../admin.css";
3
4
  export interface SeoDrawerProps {
4
5
  drawerSlug: string;
5
- keyphrase: string;
6
- setKeyphrase: (keyphrase: string) => void;
6
+ keyphrases: KeyphraseEntry[];
7
+ selectedId: string | null;
8
+ onSelect: (id: string) => void;
7
9
  result: AnalysisResult | null;
8
10
  analyzing: boolean;
9
- keyphrasePending: boolean;
10
- analyzeNow: () => void;
11
+ onAddRelated: () => void;
12
+ onTextChange: (id: string, text: string) => void;
13
+ onAddSynonym: (id: string, syn: string) => void;
14
+ onRemoveSynonym: (id: string, index: number) => void;
15
+ onRemove: (id: string) => void;
16
+ onSetFocus: (id: string) => void;
17
+ isDuplicate: (id: string, text: string) => boolean;
11
18
  site: {
12
19
  name: string;
13
20
  baseUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SeoDrawer/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,6BAA6B,CAAC;AAW/E,OAAO,iBAAiB,CAAC;AAEzB,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAED,eAAO,MAAM,SAAS,sDA6DpB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SeoDrawer/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,6BAA6B,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAWvD,OAAO,iBAAiB,CAAC;AAEzB,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAED,eAAO,MAAM,SAAS,sDAiFpB,CAAC"}
@@ -14,17 +14,25 @@ import { VitalsTab } from "./tabs/VitalsTab";
14
14
  import "../../admin.css";
15
15
  const SeoDrawer = memo(function SeoDrawer2({
16
16
  drawerSlug,
17
- keyphrase,
18
- setKeyphrase,
19
17
  result,
18
+ site,
20
19
  analyzing,
21
- keyphrasePending,
22
- analyzeNow,
23
- site
20
+ isDuplicate,
21
+ keyphrases,
22
+ onAddRelated,
23
+ onAddSynonym,
24
+ onRemove,
25
+ onRemoveSynonym,
26
+ onSelect,
27
+ onSetFocus,
28
+ onTextChange,
29
+ selectedId
24
30
  }) {
25
31
  const [tab, setTab] = useState("keyphrase");
26
32
  const total = result?.overall.seoScore ?? 0;
27
33
  const totalStatus = result ? result.overall.status : "idle";
34
+ const focusKeyphrase = keyphrases[0]?.text ?? "";
35
+ const focusSynonyms = keyphrases[0]?.synonyms ?? [];
28
36
  return /* @__PURE__ */ jsx(
29
37
  Drawer,
30
38
  {
@@ -33,7 +41,7 @@ const SeoDrawer = memo(function SeoDrawer2({
33
41
  Header: /* @__PURE__ */ jsx(Header, { drawerSlug, total, totalStatus }),
34
42
  children: /* @__PURE__ */ jsxs("div", { className: "seo-root relative text-neutral-800", "data-status": totalStatus, children: [
35
43
  /* @__PURE__ */ jsx(TabsNav, { active: tab, onChange: setTab }),
36
- /* @__PURE__ */ jsx("div", { className: "py-[18px]", children: result === null ? /* @__PURE__ */ jsxs(
44
+ /* @__PURE__ */ jsx("div", { children: result === null && tab !== "keyphrase" ? /* @__PURE__ */ jsxs(
37
45
  "div",
38
46
  {
39
47
  className: "flex items-center gap-[8px] text-neutral-500 text-[13px]",
@@ -48,19 +56,33 @@ const SeoDrawer = memo(function SeoDrawer2({
48
56
  tab === "keyphrase" && /* @__PURE__ */ jsx(
49
57
  KeyphraseTab,
50
58
  {
51
- data: result.keyphrase,
52
- keyphrase,
53
- setKeyphrase,
59
+ keyphrases,
60
+ selectedId,
61
+ onSelect,
62
+ result,
54
63
  analyzing,
55
- keyphrasePending,
56
- analyzeNow
64
+ onAddRelated,
65
+ onTextChange,
66
+ onAddSynonym,
67
+ onRemoveSynonym,
68
+ onRemove,
69
+ onSetFocus,
70
+ isDuplicate
57
71
  }
58
72
  ),
59
- tab === "onpage" && /* @__PURE__ */ jsx(OnPageTab, { data: result.onPage }),
60
- tab === "readability" && /* @__PURE__ */ jsx(ReadabilityTab, { data: result.readability }),
61
- tab === "inclusive" && /* @__PURE__ */ jsx(InclusiveTab, { data: result.inclusive }),
62
- tab === "vitals" && /* @__PURE__ */ jsx(VitalsTab, { data: result.vitals, onRequestKeyphrase: () => setTab("keyphrase") }),
63
- tab === "serp" && /* @__PURE__ */ jsx(SerpTab, { data: result.serp, keyphrase, faviconUrl: site.faviconUrl })
73
+ tab === "onpage" && result && /* @__PURE__ */ jsx(OnPageTab, { data: result.onPage }),
74
+ tab === "readability" && result && /* @__PURE__ */ jsx(ReadabilityTab, { data: result.readability }),
75
+ tab === "inclusive" && result && /* @__PURE__ */ jsx(InclusiveTab, { data: result.inclusive }),
76
+ tab === "vitals" && result && /* @__PURE__ */ jsx(VitalsTab, { data: result.vitals, onRequestKeyphrase: () => setTab("keyphrase") }),
77
+ tab === "serp" && result && /* @__PURE__ */ jsx(
78
+ SerpTab,
79
+ {
80
+ data: result.serp,
81
+ keyphrase: focusKeyphrase,
82
+ synonyms: focusSynonyms,
83
+ faviconUrl: site.faviconUrl
84
+ }
85
+ )
64
86
  ] }) })
65
87
  ] })
66
88
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/SeoDrawer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { Drawer } from \"@payloadcms/ui\";\nimport { Loader2 } from \"lucide-react\";\nimport { memo, useState } from \"react\";\nimport type { AnalysisResult, TotalStatus } from \"../../engine/types/analysis\";\nimport { Header } from \"./components/Header\";\nimport { TabsNav } from \"./TabsNav\";\nimport type { TabKey } from \"./TabsNav\";\nimport { KeyphraseTab } from \"./tabs/KeyphraseTab\";\nimport { OnPageTab } from \"./tabs/OnPageTab\";\nimport { ReadabilityTab } from \"./tabs/ReadabilityTab\";\nimport { InclusiveTab } from \"./tabs/InclusiveTab\";\nimport { SerpTab } from \"./tabs/SerpTab\";\nimport { VitalsTab } from \"./tabs/VitalsTab\";\n\nimport \"../../admin.css\";\n\nexport interface SeoDrawerProps {\n drawerSlug: string;\n keyphrase: string;\n setKeyphrase: (keyphrase: string) => void;\n result: AnalysisResult | null;\n analyzing: boolean;\n keyphrasePending: boolean;\n analyzeNow: () => void;\n site: { name: string; baseUrl: string; faviconUrl: string };\n}\n\nexport const SeoDrawer = memo(function SeoDrawer({\n drawerSlug,\n keyphrase,\n setKeyphrase,\n result,\n analyzing,\n keyphrasePending,\n analyzeNow,\n site,\n}: SeoDrawerProps) {\n const [tab, setTab] = useState<TabKey>(\"keyphrase\");\n\n const total = result?.overall.seoScore ?? 0;\n const totalStatus: TotalStatus = result ? result.overall.status : \"idle\";\n\n return (\n <Drawer\n slug={drawerSlug}\n className=\"seo-drawer\"\n Header={<Header drawerSlug={drawerSlug} total={total} totalStatus={totalStatus} />}\n >\n <div className=\"seo-root relative text-neutral-800\" data-status={totalStatus}>\n <TabsNav active={tab} onChange={setTab} />\n\n <div className=\"py-[18px]\">\n {result === null ? (\n <div\n className=\"flex items-center gap-[8px] text-neutral-500 text-[13px]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <Loader2 aria-hidden=\"true\" className=\"w-[14px] h-[14px] animate-spin\" />\n Analyzing…\n </div>\n ) : (\n <>\n {tab === \"keyphrase\" && (\n <KeyphraseTab\n data={result.keyphrase}\n keyphrase={keyphrase}\n setKeyphrase={setKeyphrase}\n analyzing={analyzing}\n keyphrasePending={keyphrasePending}\n analyzeNow={analyzeNow}\n />\n )}\n {tab === \"onpage\" && <OnPageTab data={result.onPage} />}\n {tab === \"readability\" && <ReadabilityTab data={result.readability} />}\n {tab === \"inclusive\" && <InclusiveTab data={result.inclusive} />}\n {tab === \"vitals\" && (\n <VitalsTab data={result.vitals} onRequestKeyphrase={() => setTab(\"keyphrase\")} />\n )}\n {tab === \"serp\" && (\n <SerpTab data={result.serp} keyphrase={keyphrase} faviconUrl={site.faviconUrl} />\n )}\n </>\n )}\n </div>\n </div>\n </Drawer>\n );\n});\n"],"mappings":";AAgDc,SAgBF,UAhBE,KAOF,YAPE;AA9Cd,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,MAAM,gBAAgB;AAE/B,SAAS,cAAc;AACvB,SAAS,eAAe;AAExB,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAE1B,OAAO;AAaA,MAAM,YAAY,KAAK,SAASA,WAAU;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,CAAC,KAAK,MAAM,IAAI,SAAiB,WAAW;AAElD,QAAM,QAAQ,QAAQ,QAAQ,YAAY;AAC1C,QAAM,cAA2B,SAAS,OAAO,QAAQ,SAAS;AAElE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM;AAAA,MACN,WAAU;AAAA,MACV,QAAQ,oBAAC,UAAO,YAAwB,OAAc,aAA0B;AAAA,MAEhF,+BAAC,SAAI,WAAU,sCAAqC,eAAa,aAC/D;AAAA,4BAAC,WAAQ,QAAQ,KAAK,UAAU,QAAQ;AAAA,QAExC,oBAAC,SAAI,WAAU,aACZ,qBAAW,OACV;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,MAAK;AAAA,YACL,aAAU;AAAA,YAEV;AAAA,kCAAC,WAAQ,eAAY,QAAO,WAAU,kCAAiC;AAAA,cAAE;AAAA;AAAA;AAAA,QAE3E,IAEA,iCACG;AAAA,kBAAQ,eACP;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,OAAO;AAAA,cACb;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA;AAAA,UACF;AAAA,UAED,QAAQ,YAAY,oBAAC,aAAU,MAAM,OAAO,QAAQ;AAAA,UACpD,QAAQ,iBAAiB,oBAAC,kBAAe,MAAM,OAAO,aAAa;AAAA,UACnE,QAAQ,eAAe,oBAAC,gBAAa,MAAM,OAAO,WAAW;AAAA,UAC7D,QAAQ,YACP,oBAAC,aAAU,MAAM,OAAO,QAAQ,oBAAoB,MAAM,OAAO,WAAW,GAAG;AAAA,UAEhF,QAAQ,UACP,oBAAC,WAAQ,MAAM,OAAO,MAAM,WAAsB,YAAY,KAAK,YAAY;AAAA,WAEnF,GAEJ;AAAA,SACF;AAAA;AAAA,EACF;AAEJ,CAAC;","names":["SeoDrawer"]}
1
+ {"version":3,"sources":["../../../src/components/SeoDrawer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { Drawer } from \"@payloadcms/ui\";\nimport { Loader2 } from \"lucide-react\";\nimport { memo, useState } from \"react\";\nimport type { AnalysisResult, TotalStatus } from \"../../engine/types/analysis\";\nimport type { KeyphraseEntry } from \"./keyphraseState\";\nimport { Header } from \"./components/Header\";\nimport { TabsNav } from \"./TabsNav\";\nimport type { TabKey } from \"./TabsNav\";\nimport { KeyphraseTab } from \"./tabs/KeyphraseTab\";\nimport { OnPageTab } from \"./tabs/OnPageTab\";\nimport { ReadabilityTab } from \"./tabs/ReadabilityTab\";\nimport { InclusiveTab } from \"./tabs/InclusiveTab\";\nimport { SerpTab } from \"./tabs/SerpTab\";\nimport { VitalsTab } from \"./tabs/VitalsTab\";\n\nimport \"../../admin.css\";\n\nexport interface SeoDrawerProps {\n drawerSlug: string;\n keyphrases: KeyphraseEntry[];\n selectedId: string | null;\n onSelect: (id: string) => void;\n result: AnalysisResult | null;\n analyzing: boolean;\n onAddRelated: () => void;\n onTextChange: (id: string, text: string) => void;\n onAddSynonym: (id: string, syn: string) => void;\n onRemoveSynonym: (id: string, index: number) => void;\n onRemove: (id: string) => void;\n onSetFocus: (id: string) => void;\n isDuplicate: (id: string, text: string) => boolean;\n site: { name: string; baseUrl: string; faviconUrl: string };\n}\n\nexport const SeoDrawer = memo(function SeoDrawer({\n drawerSlug,\n result,\n site,\n analyzing,\n isDuplicate,\n keyphrases,\n onAddRelated,\n onAddSynonym,\n onRemove,\n onRemoveSynonym,\n onSelect,\n onSetFocus,\n onTextChange,\n selectedId,\n}: SeoDrawerProps) {\n const [tab, setTab] = useState<TabKey>(\"keyphrase\");\n\n const total = result?.overall.seoScore ?? 0;\n const totalStatus: TotalStatus = result ? result.overall.status : \"idle\";\n\n const focusKeyphrase = keyphrases[0]?.text ?? \"\";\n const focusSynonyms = keyphrases[0]?.synonyms ?? [];\n\n return (\n <Drawer\n slug={drawerSlug}\n className=\"seo-drawer\"\n Header={<Header drawerSlug={drawerSlug} total={total} totalStatus={totalStatus} />}\n >\n <div className=\"seo-root relative text-neutral-800\" data-status={totalStatus}>\n <TabsNav active={tab} onChange={setTab} />\n\n <div>\n {result === null && tab !== \"keyphrase\" ? (\n <div\n className=\"flex items-center gap-[8px] text-neutral-500 text-[13px]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <Loader2 aria-hidden=\"true\" className=\"w-[14px] h-[14px] animate-spin\" />\n Analyzing…\n </div>\n ) : (\n <>\n {tab === \"keyphrase\" && (\n <KeyphraseTab\n keyphrases={keyphrases}\n selectedId={selectedId}\n onSelect={onSelect}\n result={result}\n analyzing={analyzing}\n onAddRelated={onAddRelated}\n onTextChange={onTextChange}\n onAddSynonym={onAddSynonym}\n onRemoveSynonym={onRemoveSynonym}\n onRemove={onRemove}\n onSetFocus={onSetFocus}\n isDuplicate={isDuplicate}\n />\n )}\n {tab === \"onpage\" && result && <OnPageTab data={result.onPage} />}\n {tab === \"readability\" && result && <ReadabilityTab data={result.readability} />}\n {tab === \"inclusive\" && result && <InclusiveTab data={result.inclusive} />}\n {tab === \"vitals\" && result && (\n <VitalsTab data={result.vitals} onRequestKeyphrase={() => setTab(\"keyphrase\")} />\n )}\n {tab === \"serp\" && result && (\n <SerpTab\n data={result.serp}\n keyphrase={focusKeyphrase}\n synonyms={focusSynonyms}\n faviconUrl={site.faviconUrl}\n />\n )}\n </>\n )}\n </div>\n </div>\n </Drawer>\n );\n});\n"],"mappings":";AAgEc,SAgBF,UAhBE,KAOF,YAPE;AA9Dd,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,MAAM,gBAAgB;AAG/B,SAAS,cAAc;AACvB,SAAS,eAAe;AAExB,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAE1B,OAAO;AAmBA,MAAM,YAAY,KAAK,SAASA,WAAU;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,CAAC,KAAK,MAAM,IAAI,SAAiB,WAAW;AAElD,QAAM,QAAQ,QAAQ,QAAQ,YAAY;AAC1C,QAAM,cAA2B,SAAS,OAAO,QAAQ,SAAS;AAElE,QAAM,iBAAiB,WAAW,CAAC,GAAG,QAAQ;AAC9C,QAAM,gBAAgB,WAAW,CAAC,GAAG,YAAY,CAAC;AAElD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM;AAAA,MACN,WAAU;AAAA,MACV,QAAQ,oBAAC,UAAO,YAAwB,OAAc,aAA0B;AAAA,MAEhF,+BAAC,SAAI,WAAU,sCAAqC,eAAa,aAC/D;AAAA,4BAAC,WAAQ,QAAQ,KAAK,UAAU,QAAQ;AAAA,QAExC,oBAAC,SACE,qBAAW,QAAQ,QAAQ,cAC1B;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,MAAK;AAAA,YACL,aAAU;AAAA,YAEV;AAAA,kCAAC,WAAQ,eAAY,QAAO,WAAU,kCAAiC;AAAA,cAAE;AAAA;AAAA;AAAA,QAE3E,IAEA,iCACG;AAAA,kBAAQ,eACP;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA;AAAA,UACF;AAAA,UAED,QAAQ,YAAY,UAAU,oBAAC,aAAU,MAAM,OAAO,QAAQ;AAAA,UAC9D,QAAQ,iBAAiB,UAAU,oBAAC,kBAAe,MAAM,OAAO,aAAa;AAAA,UAC7E,QAAQ,eAAe,UAAU,oBAAC,gBAAa,MAAM,OAAO,WAAW;AAAA,UACvE,QAAQ,YAAY,UACnB,oBAAC,aAAU,MAAM,OAAO,QAAQ,oBAAoB,MAAM,OAAO,WAAW,GAAG;AAAA,UAEhF,QAAQ,UAAU,UACjB;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,OAAO;AAAA,cACb,WAAW;AAAA,cACX,UAAU;AAAA,cACV,YAAY,KAAK;AAAA;AAAA,UACnB;AAAA,WAEJ,GAEJ;AAAA,SACF;AAAA;AAAA,EACF;AAEJ,CAAC;","names":["SeoDrawer"]}
@@ -0,0 +1,16 @@
1
+ export interface KeyphraseEntry {
2
+ id: string;
3
+ text: string;
4
+ synonyms: string[];
5
+ }
6
+ export declare function createEntry(text?: string, synonyms?: string[]): KeyphraseEntry;
7
+ export declare function firstEmptyId(list: KeyphraseEntry[]): string | undefined;
8
+ export declare function addRelated(list: KeyphraseEntry[]): KeyphraseEntry[];
9
+ export declare function updateText(list: KeyphraseEntry[], id: string, text: string): KeyphraseEntry[];
10
+ export declare function addSynonym(list: KeyphraseEntry[], id: string, syn: string): KeyphraseEntry[];
11
+ export declare function removeSynonym(list: KeyphraseEntry[], id: string, index: number): KeyphraseEntry[];
12
+ export declare function remove(list: KeyphraseEntry[], id: string): KeyphraseEntry[];
13
+ export declare function setFocus(list: KeyphraseEntry[], id: string): KeyphraseEntry[];
14
+ export declare function isDuplicate(list: KeyphraseEntry[], id: string, text: string): boolean;
15
+ export declare function pruneEmpties(list: KeyphraseEntry[]): KeyphraseEntry[];
16
+ //# sourceMappingURL=keyphraseState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyphraseState.d.ts","sourceRoot":"","sources":["../../../src/components/SeoDrawer/keyphraseState.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAUD,wBAAgB,WAAW,CAAC,IAAI,SAAK,EAAE,QAAQ,GAAE,MAAM,EAAO,GAAG,cAAc,CAM9E;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,MAAM,GAAG,SAAS,CAEvE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAKnE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAE7F;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAS5F;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,CAIjG;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,EAAE,CAE3E;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,EAAE,CAK7E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKrF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAErE"}
@@ -0,0 +1,73 @@
1
+ import { MAX_KEYPHRASES } from "../../constants";
2
+ function makeId() {
3
+ const c = globalThis.crypto;
4
+ return c?.randomUUID ? c.randomUUID() : `kp_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
5
+ }
6
+ function createEntry(text = "", synonyms = []) {
7
+ return {
8
+ id: makeId(),
9
+ text,
10
+ synonyms: [...synonyms]
11
+ };
12
+ }
13
+ function firstEmptyId(list) {
14
+ return list.find((k) => k.text.trim() === "")?.id;
15
+ }
16
+ function addRelated(list) {
17
+ if (list.length >= MAX_KEYPHRASES)
18
+ return list;
19
+ if (firstEmptyId(list))
20
+ return list;
21
+ return [...list, createEntry()];
22
+ }
23
+ function updateText(list, id, text) {
24
+ return list.map((k) => k.id === id ? { ...k, text } : k);
25
+ }
26
+ function addSynonym(list, id, syn) {
27
+ const value = syn.trim();
28
+ if (!value)
29
+ return list;
30
+ return list.map((k) => {
31
+ if (k.id !== id)
32
+ return k;
33
+ if (k.synonyms.some((s) => s.toLowerCase() === value.toLowerCase()))
34
+ return k;
35
+ return { ...k, synonyms: [...k.synonyms, value] };
36
+ });
37
+ }
38
+ function removeSynonym(list, id, index) {
39
+ return list.map(
40
+ (k) => k.id === id ? { ...k, synonyms: k.synonyms.filter((_, i) => i !== index) } : k
41
+ );
42
+ }
43
+ function remove(list, id) {
44
+ return list.filter((k) => k.id !== id);
45
+ }
46
+ function setFocus(list, id) {
47
+ const target = list.find((k) => k.id === id);
48
+ if (!target)
49
+ return list;
50
+ return [target, ...list.filter((k) => k.id !== id)];
51
+ }
52
+ function isDuplicate(list, id, text) {
53
+ const value = text.trim().toLowerCase();
54
+ if (!value)
55
+ return false;
56
+ return list.some((k) => k.id !== id && k.text.trim().toLowerCase() === value);
57
+ }
58
+ function pruneEmpties(list) {
59
+ return list.filter((k) => k.text.trim() !== "");
60
+ }
61
+ export {
62
+ addRelated,
63
+ addSynonym,
64
+ createEntry,
65
+ firstEmptyId,
66
+ isDuplicate,
67
+ pruneEmpties,
68
+ remove,
69
+ removeSynonym,
70
+ setFocus,
71
+ updateText
72
+ };
73
+ //# sourceMappingURL=keyphraseState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/SeoDrawer/keyphraseState.ts"],"sourcesContent":["import { MAX_KEYPHRASES } from \"../../constants\";\n\nexport interface KeyphraseEntry {\n id: string;\n text: string;\n synonyms: string[];\n}\n\nfunction makeId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n\n return c?.randomUUID\n ? c.randomUUID()\n : `kp_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\nexport function createEntry(text = \"\", synonyms: string[] = []): KeyphraseEntry {\n return {\n id: makeId(),\n text,\n synonyms: [...synonyms],\n };\n}\n\nexport function firstEmptyId(list: KeyphraseEntry[]): string | undefined {\n return list.find((k) => k.text.trim() === \"\")?.id;\n}\n\nexport function addRelated(list: KeyphraseEntry[]): KeyphraseEntry[] {\n if (list.length >= MAX_KEYPHRASES) return list;\n if (firstEmptyId(list)) return list;\n\n return [...list, createEntry()];\n}\n\nexport function updateText(list: KeyphraseEntry[], id: string, text: string): KeyphraseEntry[] {\n return list.map((k) => (k.id === id ? { ...k, text } : k));\n}\n\nexport function addSynonym(list: KeyphraseEntry[], id: string, syn: string): KeyphraseEntry[] {\n const value = syn.trim();\n if (!value) return list;\n\n return list.map((k) => {\n if (k.id !== id) return k;\n if (k.synonyms.some((s) => s.toLowerCase() === value.toLowerCase())) return k;\n return { ...k, synonyms: [...k.synonyms, value] };\n });\n}\n\nexport function removeSynonym(list: KeyphraseEntry[], id: string, index: number): KeyphraseEntry[] {\n return list.map((k) =>\n k.id === id ? { ...k, synonyms: k.synonyms.filter((_, i) => i !== index) } : k\n );\n}\n\nexport function remove(list: KeyphraseEntry[], id: string): KeyphraseEntry[] {\n return list.filter((k) => k.id !== id);\n}\n\nexport function setFocus(list: KeyphraseEntry[], id: string): KeyphraseEntry[] {\n const target = list.find((k) => k.id === id);\n if (!target) return list;\n\n return [target, ...list.filter((k) => k.id !== id)];\n}\n\nexport function isDuplicate(list: KeyphraseEntry[], id: string, text: string): boolean {\n const value = text.trim().toLowerCase();\n if (!value) return false;\n\n return list.some((k) => k.id !== id && k.text.trim().toLowerCase() === value);\n}\n\nexport function pruneEmpties(list: KeyphraseEntry[]): KeyphraseEntry[] {\n return list.filter((k) => k.text.trim() !== \"\");\n}\n"],"mappings":"AAAA,SAAS,sBAAsB;AAQ/B,SAAS,SAAiB;AACxB,QAAM,IAAK,WAA0D;AAErE,SAAO,GAAG,aACN,EAAE,WAAW,IACb,MAAM,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC;AACzE;AAEO,SAAS,YAAY,OAAO,IAAI,WAAqB,CAAC,GAAmB;AAC9E,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX;AAAA,IACA,UAAU,CAAC,GAAG,QAAQ;AAAA,EACxB;AACF;AAEO,SAAS,aAAa,MAA4C;AACvE,SAAO,KAAK,KAAK,CAAC,MAAM,EAAE,KAAK,KAAK,MAAM,EAAE,GAAG;AACjD;AAEO,SAAS,WAAW,MAA0C;AACnE,MAAI,KAAK,UAAU;AAAgB,WAAO;AAC1C,MAAI,aAAa,IAAI;AAAG,WAAO;AAE/B,SAAO,CAAC,GAAG,MAAM,YAAY,CAAC;AAChC;AAEO,SAAS,WAAW,MAAwB,IAAY,MAAgC;AAC7F,SAAO,KAAK,IAAI,CAAC,MAAO,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,CAAE;AAC3D;AAEO,SAAS,WAAW,MAAwB,IAAY,KAA+B;AAC5F,QAAM,QAAQ,IAAI,KAAK;AACvB,MAAI,CAAC;AAAO,WAAO;AAEnB,SAAO,KAAK,IAAI,CAAC,MAAM;AACrB,QAAI,EAAE,OAAO;AAAI,aAAO;AACxB,QAAI,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,MAAM,YAAY,CAAC;AAAG,aAAO;AAC5E,WAAO,EAAE,GAAG,GAAG,UAAU,CAAC,GAAG,EAAE,UAAU,KAAK,EAAE;AAAA,EAClD,CAAC;AACH;AAEO,SAAS,cAAc,MAAwB,IAAY,OAAiC;AACjG,SAAO,KAAK;AAAA,IAAI,CAAC,MACf,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,UAAU,EAAE,SAAS,OAAO,CAAC,GAAG,MAAM,MAAM,KAAK,EAAE,IAAI;AAAA,EAC/E;AACF;AAEO,SAAS,OAAO,MAAwB,IAA8B;AAC3E,SAAO,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACvC;AAEO,SAAS,SAAS,MAAwB,IAA8B;AAC7E,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC3C,MAAI,CAAC;AAAQ,WAAO;AAEpB,SAAO,CAAC,QAAQ,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;AACpD;AAEO,SAAS,YAAY,MAAwB,IAAY,MAAuB;AACrF,QAAM,QAAQ,KAAK,KAAK,EAAE,YAAY;AACtC,MAAI,CAAC;AAAO,WAAO;AAEnB,SAAO,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,KAAK,KAAK,EAAE,YAAY,MAAM,KAAK;AAC9E;AAEO,SAAS,aAAa,MAA0C;AACrE,SAAO,KAAK,OAAO,CAAC,MAAM,EAAE,KAAK,KAAK,MAAM,EAAE;AAChD;","names":[]}
@@ -0,0 +1,5 @@
1
+ import type { KeyphraseEntry } from './keyphraseState';
2
+ export declare function storageKey(collectionSlug: string, docId: string, locale: string): string;
3
+ export declare function loadKeyphrases(key: string): KeyphraseEntry[];
4
+ export declare function saveKeyphrases(key: string, list: KeyphraseEntry[]): void;
5
+ //# sourceMappingURL=keyphraseStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyphraseStorage.d.ts","sourceRoot":"","sources":["../../../src/components/SeoDrawer/keyphraseStorage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AASvD,wBAAgB,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAExF;AAWD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAyB5D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,IAAI,CAmBxE"}
@@ -0,0 +1,60 @@
1
+ import { createEntry, pruneEmpties } from "./keyphraseState";
2
+ const PREFIX = "seo-kw";
3
+ function storageKey(collectionSlug, docId, locale) {
4
+ return `${PREFIX}:${collectionSlug}:${docId}:${locale}`;
5
+ }
6
+ function store() {
7
+ try {
8
+ if (typeof window === "undefined" || !window.localStorage)
9
+ return null;
10
+ return window.localStorage;
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+ function loadKeyphrases(key) {
16
+ const fallback = () => [createEntry()];
17
+ const s = store();
18
+ if (!s)
19
+ return fallback();
20
+ try {
21
+ const raw = s.getItem(key);
22
+ if (!raw)
23
+ return fallback();
24
+ const parsed = JSON.parse(raw);
25
+ if (!Array.isArray(parsed) || parsed.length === 0)
26
+ return fallback();
27
+ const list = parsed.filter((e) => e && typeof e.text === "string").map(
28
+ (e) => createEntry(
29
+ e.text,
30
+ Array.isArray(e.synonyms) ? e.synonyms.filter((x) => typeof x === "string") : []
31
+ )
32
+ );
33
+ return list.length ? list : fallback();
34
+ } catch {
35
+ return fallback();
36
+ }
37
+ }
38
+ function saveKeyphrases(key, list) {
39
+ const s = store();
40
+ if (!s)
41
+ return;
42
+ try {
43
+ const persistable = pruneEmpties(list).map((k) => ({
44
+ text: k.text,
45
+ synonyms: k.synonyms
46
+ }));
47
+ if (persistable.length === 0) {
48
+ s.removeItem(key);
49
+ return;
50
+ }
51
+ s.setItem(key, JSON.stringify(persistable));
52
+ } catch {
53
+ }
54
+ }
55
+ export {
56
+ loadKeyphrases,
57
+ saveKeyphrases,
58
+ storageKey
59
+ };
60
+ //# sourceMappingURL=keyphraseStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/SeoDrawer/keyphraseStorage.ts"],"sourcesContent":["import { createEntry, pruneEmpties } from './keyphraseState';\nimport type { KeyphraseEntry } from './keyphraseState';\n\nconst PREFIX = \"seo-kw\";\n\ninterface StoredEntry {\n text: string;\n synonyms: string[];\n}\n\nexport function storageKey(collectionSlug: string, docId: string, locale: string): string {\n return `${PREFIX}:${collectionSlug}:${docId}:${locale}`;\n}\n\nfunction store(): Storage | null {\n try {\n if (typeof window === \"undefined\" || !window.localStorage) return null;\n return window.localStorage;\n } catch {\n return null;\n }\n}\n\nexport function loadKeyphrases(key: string): KeyphraseEntry[] {\n const fallback = () => [createEntry()];\n const s = store();\n if (!s) return fallback();\n\n try {\n const raw = s.getItem(key);\n if (!raw) return fallback();\n\n const parsed = JSON.parse(raw) as StoredEntry[];\n if (!Array.isArray(parsed) || parsed.length === 0) return fallback();\n\n const list = parsed\n .filter((e) => e && typeof e.text === \"string\")\n .map((e) =>\n createEntry(\n e.text,\n Array.isArray(e.synonyms) ? e.synonyms.filter((x) => typeof x === \"string\") : []\n )\n );\n\n return list.length ? list : fallback();\n } catch {\n return fallback();\n }\n}\n\nexport function saveKeyphrases(key: string, list: KeyphraseEntry[]): void {\n const s = store();\n if (!s) return;\n\n try {\n const persistable = pruneEmpties(list).map<StoredEntry>((k) => ({\n text: k.text,\n synonyms: k.synonyms,\n }));\n\n if (persistable.length === 0) {\n s.removeItem(key);\n return;\n }\n\n s.setItem(key, JSON.stringify(persistable));\n } catch {\n // quota / serialization — fail soft\n }\n}\n"],"mappings":"AAAA,SAAS,aAAa,oBAAoB;AAG1C,MAAM,SAAS;AAOR,SAAS,WAAW,gBAAwB,OAAe,QAAwB;AACxF,SAAO,GAAG,MAAM,IAAI,cAAc,IAAI,KAAK,IAAI,MAAM;AACvD;AAEA,SAAS,QAAwB;AAC/B,MAAI;AACF,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO;AAAc,aAAO;AAClE,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,KAA+B;AAC5D,QAAM,WAAW,MAAM,CAAC,YAAY,CAAC;AACrC,QAAM,IAAI,MAAM;AAChB,MAAI,CAAC;AAAG,WAAO,SAAS;AAExB,MAAI;AACF,UAAM,MAAM,EAAE,QAAQ,GAAG;AACzB,QAAI,CAAC;AAAK,aAAO,SAAS;AAE1B,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW;AAAG,aAAO,SAAS;AAEnE,UAAM,OAAO,OACV,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,SAAS,QAAQ,EAC7C;AAAA,MAAI,CAAC,MACJ;AAAA,QACE,EAAE;AAAA,QACF,MAAM,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS,OAAO,CAAC,MAAM,OAAO,MAAM,QAAQ,IAAI,CAAC;AAAA,MACjF;AAAA,IACF;AAEF,WAAO,KAAK,SAAS,OAAO,SAAS;AAAA,EACvC,QAAQ;AACN,WAAO,SAAS;AAAA,EAClB;AACF;AAEO,SAAS,eAAe,KAAa,MAA8B;AACxE,QAAM,IAAI,MAAM;AAChB,MAAI,CAAC;AAAG;AAER,MAAI;AACF,UAAM,cAAc,aAAa,IAAI,EAAE,IAAiB,CAAC,OAAO;AAAA,MAC9D,MAAM,EAAE;AAAA,MACR,UAAU,EAAE;AAAA,IACd,EAAE;AAEF,QAAI,YAAY,WAAW,GAAG;AAC5B,QAAE,WAAW,GAAG;AAChB;AAAA,IACF;AAEA,MAAE,QAAQ,KAAK,KAAK,UAAU,WAAW,CAAC;AAAA,EAC5C,QAAQ;AAAA,EAER;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"InclusiveTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/InclusiveTab.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAMrE,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC,CAAA;CAAE,2CA8D3E"}
1
+ {"version":3,"file":"InclusiveTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/InclusiveTab.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAOrE,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC,CAAA;CAAE,2CAgE3E"}
@@ -4,9 +4,10 @@ import { cn, ROW_SEPARATOR } from "../../../utils/style";
4
4
  import { TabHeader } from "../../../ui/TabHeader";
5
5
  import { SectionCard } from "../../../ui/SectionCard";
6
6
  import { Pill } from "../../../ui/Pill";
7
+ import { TabWrapper } from "../components/TabWrapper";
7
8
  function InclusiveTab({ data }) {
8
9
  const flagged = data.categories.reduce((n, c) => n + c.flags.length, 0);
9
- return /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-[13px]", children: [
10
+ return /* @__PURE__ */ jsx(TabWrapper, { children: /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-[13px]", children: [
10
11
  /* @__PURE__ */ jsx(
11
12
  TabHeader,
12
13
  {
@@ -59,7 +60,7 @@ function InclusiveTab({ data }) {
59
60
  )) })
60
61
  }
61
62
  )
62
- ] });
63
+ ] }) });
63
64
  }
64
65
  export {
65
66
  InclusiveTab
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/InclusiveTab.tsx"],"sourcesContent":["\"use client\";\n\nimport type { AnalysisResult } from \"../../../engine/types/analysis\";\nimport { cn, ROW_SEPARATOR } from \"../../../utils/style\";\nimport { TabHeader } from \"../../../ui/TabHeader\";\nimport { SectionCard } from \"../../../ui/SectionCard\";\nimport { Pill } from \"../../../ui/Pill\";\n\nexport function InclusiveTab({ data }: { data: AnalysisResult[\"inclusive\"] }) {\n const flagged = data.categories.reduce((n, c) => n + c.flags.length, 0);\n\n return (\n <section className=\"flex flex-col gap-[13px]\">\n <TabHeader\n title=\"Inclusive language\"\n score={data.ringScore}\n status={data.status}\n statusLabel={data.status === \"good\" ? \"Good\" : \"Needs work\"}\n subtitle={\n <>\n {flagged} phrases flagged across {data.categories.length} categories\n </>\n }\n />\n\n {data.categories.length > 0 && (\n <SectionCard title=\"Marked by category\" widget={<Pill variant=\"neutral\">{flagged}</Pill>}>\n {data.categories.map((cat) => (\n <div className={cn(\"relative px-[15px] py-[12px]\", ROW_SEPARATOR)} key={cat.name}>\n <div className=\"flex items-center gap-[8px] mb-[9px]\">\n <span className=\"font-bold text-[12.5px]\">{cat.name}</span>\n <Pill variant=\"bad\">{cat.flags.length}</Pill>\n </div>\n {cat.flags.map((f, i) => (\n <div\n className=\"flex items-center gap-[9px] py-[6px] text-[12px]\"\n key={`${f.term}-${i}`}\n >\n <span className=\"text-seo-bad font-medium whitespace-nowrap\">{f.term}</span>\n <span className=\"text-neutral-300\">›</span>\n <span className=\"text-seo-good font-medium flex-1\">{f.suggestion}</span>\n <span className=\"text-neutral-500 font-mono text-[10.5px] whitespace-nowrap\">\n {f.location}\n </span>\n </div>\n ))}\n </div>\n ))}\n </SectionCard>\n )}\n\n {data.cleanCategories.length > 0 && (\n <SectionCard\n title=\"No issues found\"\n widget={<Pill variant=\"neutral\">{data.cleanCategories.length}</Pill>}\n >\n <div className=\"flex gap-[8px] flex-wrap px-[15px] py-[13px]\">\n {data.cleanCategories.map((n) => (\n <span\n className=\"inline-flex items-center gap-[6px] text-[11.5px] text-seo-good bg-seo-good-100 border border-seo-good-200 rounded-[20px] px-[11px] py-[4px] font-medium\"\n key={n}\n >\n ✓ {n}\n </span>\n ))}\n </div>\n </SectionCard>\n )}\n </section>\n );\n}\n"],"mappings":";AAaM,SAMI,UANJ,KAMI,YANJ;AAVN,SAAS,IAAI,qBAAqB;AAClC,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AAEd,SAAS,aAAa,EAAE,KAAK,GAA0C;AAC5E,QAAM,UAAU,KAAK,WAAW,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEtE,SACE,qBAAC,aAAQ,WAAU,4BACjB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,aAAa,KAAK,WAAW,SAAS,SAAS;AAAA,QAC/C,UACE,iCACG;AAAA;AAAA,UAAQ;AAAA,UAAyB,KAAK,WAAW;AAAA,UAAO;AAAA,WAC3D;AAAA;AAAA,IAEJ;AAAA,IAEC,KAAK,WAAW,SAAS,KACxB,oBAAC,eAAY,OAAM,sBAAqB,QAAQ,oBAAC,QAAK,SAAQ,WAAW,mBAAQ,GAC9E,eAAK,WAAW,IAAI,CAAC,QACpB,qBAAC,SAAI,WAAW,GAAG,gCAAgC,aAAa,GAC9D;AAAA,2BAAC,SAAI,WAAU,wCACb;AAAA,4BAAC,UAAK,WAAU,2BAA2B,cAAI,MAAK;AAAA,QACpD,oBAAC,QAAK,SAAQ,OAAO,cAAI,MAAM,QAAO;AAAA,SACxC;AAAA,MACC,IAAI,MAAM,IAAI,CAAC,GAAG,MACjB;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UAGV;AAAA,gCAAC,UAAK,WAAU,8CAA8C,YAAE,MAAK;AAAA,YACrE,oBAAC,UAAK,WAAU,oBAAmB,oBAAC;AAAA,YACpC,oBAAC,UAAK,WAAU,oCAAoC,YAAE,YAAW;AAAA,YACjE,oBAAC,UAAK,WAAU,8DACb,YAAE,UACL;AAAA;AAAA;AAAA,QAPK,GAAG,EAAE,IAAI,IAAI,CAAC;AAAA,MAQrB,CACD;AAAA,SAjBqE,IAAI,IAkB5E,CACD,GACH;AAAA,IAGD,KAAK,gBAAgB,SAAS,KAC7B;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,QAAQ,oBAAC,QAAK,SAAQ,WAAW,eAAK,gBAAgB,QAAO;AAAA,QAE7D,8BAAC,SAAI,WAAU,gDACZ,eAAK,gBAAgB,IAAI,CAAC,MACzB;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YAEX;AAAA;AAAA,cACI;AAAA;AAAA;AAAA,UAFE;AAAA,QAGP,CACD,GACH;AAAA;AAAA,IACF;AAAA,KAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/InclusiveTab.tsx"],"sourcesContent":["\"use client\";\n\nimport type { AnalysisResult } from \"../../../engine/types/analysis\";\nimport { cn, ROW_SEPARATOR } from \"../../../utils/style\";\nimport { TabHeader } from \"../../../ui/TabHeader\";\nimport { SectionCard } from \"../../../ui/SectionCard\";\nimport { Pill } from \"../../../ui/Pill\";\nimport { TabWrapper } from \"../components/TabWrapper\";\n\nexport function InclusiveTab({ data }: { data: AnalysisResult[\"inclusive\"] }) {\n const flagged = data.categories.reduce((n, c) => n + c.flags.length, 0);\n\n return (\n <TabWrapper>\n <section className=\"flex flex-col gap-[13px]\">\n <TabHeader\n title=\"Inclusive language\"\n score={data.ringScore}\n status={data.status}\n statusLabel={data.status === \"good\" ? \"Good\" : \"Needs work\"}\n subtitle={\n <>\n {flagged} phrases flagged across {data.categories.length} categories\n </>\n }\n />\n\n {data.categories.length > 0 && (\n <SectionCard title=\"Marked by category\" widget={<Pill variant=\"neutral\">{flagged}</Pill>}>\n {data.categories.map((cat) => (\n <div className={cn(\"relative px-[15px] py-[12px]\", ROW_SEPARATOR)} key={cat.name}>\n <div className=\"flex items-center gap-[8px] mb-[9px]\">\n <span className=\"font-bold text-[12.5px]\">{cat.name}</span>\n <Pill variant=\"bad\">{cat.flags.length}</Pill>\n </div>\n {cat.flags.map((f, i) => (\n <div\n className=\"flex items-center gap-[9px] py-[6px] text-[12px]\"\n key={`${f.term}-${i}`}\n >\n <span className=\"text-seo-bad font-medium whitespace-nowrap\">{f.term}</span>\n <span className=\"text-neutral-300\">›</span>\n <span className=\"text-seo-good font-medium flex-1\">{f.suggestion}</span>\n <span className=\"text-neutral-500 font-mono text-[10.5px] whitespace-nowrap\">\n {f.location}\n </span>\n </div>\n ))}\n </div>\n ))}\n </SectionCard>\n )}\n\n {data.cleanCategories.length > 0 && (\n <SectionCard\n title=\"No issues found\"\n widget={<Pill variant=\"neutral\">{data.cleanCategories.length}</Pill>}\n >\n <div className=\"flex gap-[8px] flex-wrap px-[15px] py-[13px]\">\n {data.cleanCategories.map((n) => (\n <span\n className=\"inline-flex items-center gap-[6px] text-[11.5px] text-seo-good bg-seo-good-100 border border-seo-good-200 rounded-[20px] px-[11px] py-[4px] font-medium\"\n key={n}\n >\n ✓ {n}\n </span>\n ))}\n </div>\n </SectionCard>\n )}\n </section>\n </TabWrapper>\n );\n}\n"],"mappings":";AAeQ,SAMI,UANJ,KAMI,YANJ;AAZR,SAAS,IAAI,qBAAqB;AAClC,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAEpB,SAAS,aAAa,EAAE,KAAK,GAA0C;AAC5E,QAAM,UAAU,KAAK,WAAW,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEtE,SACE,oBAAC,cACC,+BAAC,aAAQ,WAAU,4BACjB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,aAAa,KAAK,WAAW,SAAS,SAAS;AAAA,QAC/C,UACE,iCACG;AAAA;AAAA,UAAQ;AAAA,UAAyB,KAAK,WAAW;AAAA,UAAO;AAAA,WAC3D;AAAA;AAAA,IAEJ;AAAA,IAEC,KAAK,WAAW,SAAS,KACxB,oBAAC,eAAY,OAAM,sBAAqB,QAAQ,oBAAC,QAAK,SAAQ,WAAW,mBAAQ,GAC9E,eAAK,WAAW,IAAI,CAAC,QACpB,qBAAC,SAAI,WAAW,GAAG,gCAAgC,aAAa,GAC9D;AAAA,2BAAC,SAAI,WAAU,wCACb;AAAA,4BAAC,UAAK,WAAU,2BAA2B,cAAI,MAAK;AAAA,QACpD,oBAAC,QAAK,SAAQ,OAAO,cAAI,MAAM,QAAO;AAAA,SACxC;AAAA,MACC,IAAI,MAAM,IAAI,CAAC,GAAG,MACjB;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UAGV;AAAA,gCAAC,UAAK,WAAU,8CAA8C,YAAE,MAAK;AAAA,YACrE,oBAAC,UAAK,WAAU,oBAAmB,oBAAC;AAAA,YACpC,oBAAC,UAAK,WAAU,oCAAoC,YAAE,YAAW;AAAA,YACjE,oBAAC,UAAK,WAAU,8DACb,YAAE,UACL;AAAA;AAAA;AAAA,QAPK,GAAG,EAAE,IAAI,IAAI,CAAC;AAAA,MAQrB,CACD;AAAA,SAjBqE,IAAI,IAkB5E,CACD,GACH;AAAA,IAGD,KAAK,gBAAgB,SAAS,KAC7B;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,QAAQ,oBAAC,QAAK,SAAQ,WAAW,eAAK,gBAAgB,QAAO;AAAA,QAE7D,8BAAC,SAAI,WAAU,gDACZ,eAAK,gBAAgB,IAAI,CAAC,MACzB;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YAEX;AAAA;AAAA,cACI;AAAA;AAAA;AAAA,UAFE;AAAA,QAGP,CACD,GACH;AAAA;AAAA,IACF;AAAA,KAEJ,GACF;AAEJ;","names":[]}
@@ -1,11 +1,18 @@
1
- import type { CategoryResult } from "../../../engine/types/analysis";
1
+ import type { AnalysisResult } from "../../../engine/types/analysis";
2
+ import type { KeyphraseEntry } from "../keyphraseState";
2
3
  export interface KeyphraseTabProps {
3
- data: CategoryResult;
4
- keyphrase: string;
5
- setKeyphrase: (keyphrase: string) => void;
4
+ keyphrases: KeyphraseEntry[];
5
+ selectedId: string | null;
6
+ onSelect: (id: string) => void;
7
+ result: AnalysisResult | null;
6
8
  analyzing: boolean;
7
- keyphrasePending: boolean;
8
- analyzeNow: () => void;
9
+ onAddRelated: () => void;
10
+ onTextChange: (id: string, text: string) => void;
11
+ onAddSynonym: (id: string, syn: string) => void;
12
+ onRemoveSynonym: (id: string, index: number) => void;
13
+ onRemove: (id: string) => void;
14
+ onSetFocus: (id: string) => void;
15
+ isDuplicate: (id: string, text: string) => boolean;
9
16
  }
10
- export declare function KeyphraseTab({ data, keyphrase, setKeyphrase, analyzing, keyphrasePending, analyzeNow, }: KeyphraseTabProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function KeyphraseTab({ keyphrases, selectedId: selectedIdProp, onSelect, result, analyzing, onAddRelated, onTextChange, onAddSynonym, onRemoveSynonym, onRemove, onSetFocus, isDuplicate, }: KeyphraseTabProps): import("react/jsx-runtime").JSX.Element;
11
18
  //# sourceMappingURL=KeyphraseTab.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"KeyphraseTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/KeyphraseTab.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAQrE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,UAAU,GACX,EAAE,iBAAiB,2CAqEnB"}
1
+ {"version":3,"file":"KeyphraseTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/KeyphraseTab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,gCAAgC,CAAC;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMxD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACpD;AAYD,wBAAgB,YAAY,CAAC,EAC3B,UAAU,EACV,UAAU,EAAE,cAAc,EAC1B,QAAQ,EACR,MAAM,EACN,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,EAAE,iBAAiB,2CAkGnB"}