@focus-reactive/payload-plugin-seo 1.0.2 → 1.2.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 (66) hide show
  1. package/dist/admin.css +172 -11
  2. package/dist/components/SeoButton/ScoreBadge.d.ts +9 -0
  3. package/dist/components/SeoButton/ScoreBadge.d.ts.map +1 -0
  4. package/dist/components/SeoButton/ScoreBadge.js +20 -0
  5. package/dist/components/SeoButton/ScoreBadge.js.map +1 -0
  6. package/dist/components/SeoButton/SeoButtonInner.d.ts +13 -0
  7. package/dist/components/SeoButton/SeoButtonInner.d.ts.map +1 -0
  8. package/dist/components/SeoButton/SeoButtonInner.js +68 -0
  9. package/dist/components/SeoButton/SeoButtonInner.js.map +1 -0
  10. package/dist/components/SeoButton/index.d.ts +3 -12
  11. package/dist/components/SeoButton/index.d.ts.map +1 -1
  12. package/dist/components/SeoButton/index.js +9 -73
  13. package/dist/components/SeoButton/index.js.map +1 -1
  14. package/dist/components/SeoButton/isExistingDocument.d.ts +2 -0
  15. package/dist/components/SeoButton/isExistingDocument.d.ts.map +1 -0
  16. package/dist/components/SeoButton/isExistingDocument.js +11 -0
  17. package/dist/components/SeoButton/isExistingDocument.js.map +1 -0
  18. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingLevelTiles.d.ts +7 -0
  19. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingLevelTiles.d.ts.map +1 -0
  20. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingLevelTiles.js +29 -0
  21. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingLevelTiles.js.map +1 -0
  22. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/Chevron.d.ts +4 -0
  23. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/Chevron.d.ts.map +1 -0
  24. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/Chevron.js +10 -0
  25. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/Chevron.js.map +1 -0
  26. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeGroup.d.ts +10 -0
  27. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeGroup.d.ts.map +1 -0
  28. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeGroup.js +50 -0
  29. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeGroup.js.map +1 -0
  30. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.d.ts +15 -0
  31. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.d.ts.map +1 -0
  32. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.js +38 -0
  33. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.js.map +1 -0
  34. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.d.ts +3 -0
  35. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.d.ts.map +1 -0
  36. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.js +17 -0
  37. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.js.map +1 -0
  38. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/index.d.ts +6 -0
  39. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/index.d.ts.map +1 -0
  40. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/index.js +41 -0
  41. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/index.js.map +1 -0
  42. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/useHeadingRails.d.ts +32 -0
  43. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/useHeadingRails.d.ts.map +1 -0
  44. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/useHeadingRails.js +84 -0
  45. package/dist/components/SeoDrawer/components/HeadingsSection/HeadingTree/useHeadingRails.js.map +1 -0
  46. package/dist/components/SeoDrawer/components/HeadingsSection/index.d.ts +7 -0
  47. package/dist/components/SeoDrawer/components/HeadingsSection/index.d.ts.map +1 -0
  48. package/dist/components/SeoDrawer/components/HeadingsSection/index.js +17 -0
  49. package/dist/components/SeoDrawer/components/HeadingsSection/index.js.map +1 -0
  50. package/dist/components/SeoDrawer/tabs/VitalsTab.d.ts.map +1 -1
  51. package/dist/components/SeoDrawer/tabs/VitalsTab.js +2 -0
  52. package/dist/components/SeoDrawer/tabs/VitalsTab.js.map +1 -1
  53. package/dist/engine/runAnalysis/services/derive-vitals/heading-tree.d.ts +7 -0
  54. package/dist/engine/runAnalysis/services/derive-vitals/heading-tree.d.ts.map +1 -0
  55. package/dist/engine/runAnalysis/services/derive-vitals/heading-tree.js +41 -0
  56. package/dist/engine/runAnalysis/services/derive-vitals/heading-tree.js.map +1 -0
  57. package/dist/engine/runAnalysis/services/derive-vitals/headings.d.ts +8 -0
  58. package/dist/engine/runAnalysis/services/derive-vitals/headings.d.ts.map +1 -0
  59. package/dist/engine/runAnalysis/services/derive-vitals/headings.js +50 -0
  60. package/dist/engine/runAnalysis/services/derive-vitals/headings.js.map +1 -0
  61. package/dist/engine/runAnalysis/services/derive-vitals/index.d.ts.map +1 -1
  62. package/dist/engine/runAnalysis/services/derive-vitals/index.js +4 -1
  63. package/dist/engine/runAnalysis/services/derive-vitals/index.js.map +1 -1
  64. package/dist/engine/types/analysis.d.ts +17 -0
  65. package/dist/engine/types/analysis.d.ts.map +1 -1
  66. package/package.json +1 -1
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Button, useModal } from "@payloadcms/ui";
4
+ import { Gauge } from "lucide-react";
5
+ import { useCallback, useState } from "react";
6
+ import { ScoreBadge } from "./ScoreBadge";
7
+ import { SeoDrawer } from "../SeoDrawer";
8
+ import { isKeyphrasePending } from "../SeoDrawer/keyphrasePending";
9
+ import { useAnalysis } from "../SeoDrawer/useAnalysis";
10
+ import { useLiveDocument } from "../SeoDrawer/useLiveDocument";
11
+ const DRAWER_SLUG = "seo-analytics-drawer";
12
+ function SeoButtonInner({ collectionSlug, fields, site, supportedLocales }) {
13
+ const { openModal } = useModal();
14
+ const [keyphrase, setKeyphrase] = useState("");
15
+ const { signature, getInput, invalidateMedia } = useLiveDocument({
16
+ collectionSlug,
17
+ fields,
18
+ site: { name: site.name, baseUrl: site.baseUrl },
19
+ keyphrase
20
+ });
21
+ const { result, analyzing, analyzedKeyphrase, analyzeNow } = useAnalysis({
22
+ getInput,
23
+ signature,
24
+ supportedLocales
25
+ });
26
+ const keyphrasePending = isKeyphrasePending(keyphrase, analyzedKeyphrase);
27
+ const overall = result?.overall ?? null;
28
+ const open = useCallback(() => {
29
+ invalidateMedia();
30
+ analyzeNow();
31
+ openModal(DRAWER_SLUG);
32
+ }, [analyzeNow, invalidateMedia, openModal]);
33
+ return /* @__PURE__ */ jsxs("span", { className: "relative inline-flex", children: [
34
+ /* @__PURE__ */ jsx(
35
+ Button,
36
+ {
37
+ "aria-label": "SEO Analytics",
38
+ buttonStyle: "none",
39
+ className: "seo-doc-btn m-0 w-[calc(var(--base)*1.6)] h-[calc(var(--base)*1.6)] inline-flex items-center justify-center border border-[var(--theme-elevation-100)] rounded-rs bg-transparent text-neutral-800 transition-[border-color,background-color] duration-100 hover:border-neutral-300 hover:bg-neutral-100",
40
+ extraButtonProps: { title: void 0 },
41
+ icon: /* @__PURE__ */ jsx(Gauge, {}),
42
+ iconStyle: "without-border",
43
+ margin: false,
44
+ onClick: open,
45
+ size: "small",
46
+ tooltip: "SEO Analytics"
47
+ }
48
+ ),
49
+ overall && /* @__PURE__ */ jsx(ScoreBadge, { score: overall.seoScore, status: overall.status }),
50
+ /* @__PURE__ */ jsx(
51
+ SeoDrawer,
52
+ {
53
+ analyzeNow,
54
+ analyzing,
55
+ drawerSlug: DRAWER_SLUG,
56
+ keyphrase,
57
+ keyphrasePending,
58
+ result,
59
+ setKeyphrase,
60
+ site
61
+ }
62
+ )
63
+ ] });
64
+ }
65
+ export {
66
+ SeoButtonInner
67
+ };
68
+ //# sourceMappingURL=SeoButtonInner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/SeoButton/SeoButtonInner.tsx"],"sourcesContent":["\"use client\";\n\nimport { Button, useModal } from \"@payloadcms/ui\";\nimport { Gauge } from \"lucide-react\";\nimport { useCallback, useState } from \"react\";\nimport { ScoreBadge } from \"./ScoreBadge\";\nimport { SeoDrawer } from \"../SeoDrawer\";\nimport { isKeyphrasePending } from \"../SeoDrawer/keyphrasePending\";\nimport { useAnalysis } from \"../SeoDrawer/useAnalysis\";\nimport { useLiveDocument } from \"../SeoDrawer/useLiveDocument\";\n\nexport interface SeoButtonProps {\n collectionSlug: string;\n fields: Record<string, string>;\n extractContentPath: string | null;\n site: { name: string; baseUrl: string; faviconUrl: string };\n supportedLocales: string[];\n}\n\nconst DRAWER_SLUG = \"seo-analytics-drawer\";\n\nexport function SeoButtonInner({ collectionSlug, fields, site, supportedLocales }: SeoButtonProps) {\n const { openModal } = useModal();\n const [keyphrase, setKeyphrase] = useState(\"\");\n\n const { signature, getInput, invalidateMedia } = useLiveDocument({\n collectionSlug,\n fields,\n site: { name: site.name, baseUrl: site.baseUrl },\n keyphrase,\n });\n const { result, analyzing, analyzedKeyphrase, analyzeNow } = useAnalysis({\n getInput,\n signature,\n supportedLocales,\n });\n\n const keyphrasePending = isKeyphrasePending(keyphrase, analyzedKeyphrase);\n const overall = result?.overall ?? null;\n\n const open = useCallback(() => {\n invalidateMedia();\n analyzeNow();\n openModal(DRAWER_SLUG);\n }, [analyzeNow, invalidateMedia, openModal]);\n\n return (\n <span className=\"relative inline-flex\">\n <Button\n aria-label=\"SEO Analytics\"\n buttonStyle=\"none\"\n className=\"seo-doc-btn m-0 w-[calc(var(--base)*1.6)] h-[calc(var(--base)*1.6)] inline-flex items-center justify-center border border-[var(--theme-elevation-100)] rounded-rs bg-transparent text-neutral-800 transition-[border-color,background-color] duration-100 hover:border-neutral-300 hover:bg-neutral-100\"\n extraButtonProps={{ title: undefined }}\n icon={<Gauge />}\n iconStyle=\"without-border\"\n margin={false}\n onClick={open}\n size=\"small\"\n tooltip=\"SEO Analytics\"\n />\n {overall && <ScoreBadge score={overall.seoScore} status={overall.status} />}\n\n <SeoDrawer\n analyzeNow={analyzeNow}\n analyzing={analyzing}\n drawerSlug={DRAWER_SLUG}\n keyphrase={keyphrase}\n keyphrasePending={keyphrasePending}\n result={result}\n setKeyphrase={setKeyphrase}\n site={site}\n />\n </span>\n );\n}\n"],"mappings":";AA+CI,SAMU,KANV;AA7CJ,SAAS,QAAQ,gBAAgB;AACjC,SAAS,aAAa;AACtB,SAAS,aAAa,gBAAgB;AACtC,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAC1B,SAAS,0BAA0B;AACnC,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAUhC,MAAM,cAAc;AAEb,SAAS,eAAe,EAAE,gBAAgB,QAAQ,MAAM,iBAAiB,GAAmB;AACjG,QAAM,EAAE,UAAU,IAAI,SAAS;AAC/B,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,EAAE;AAE7C,QAAM,EAAE,WAAW,UAAU,gBAAgB,IAAI,gBAAgB;AAAA,IAC/D;AAAA,IACA;AAAA,IACA,MAAM,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,QAAQ;AAAA,IAC/C;AAAA,EACF,CAAC;AACD,QAAM,EAAE,QAAQ,WAAW,mBAAmB,WAAW,IAAI,YAAY;AAAA,IACvE;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,mBAAmB,mBAAmB,WAAW,iBAAiB;AACxE,QAAM,UAAU,QAAQ,WAAW;AAEnC,QAAM,OAAO,YAAY,MAAM;AAC7B,oBAAgB;AAChB,eAAW;AACX,cAAU,WAAW;AAAA,EACvB,GAAG,CAAC,YAAY,iBAAiB,SAAS,CAAC;AAE3C,SACE,qBAAC,UAAK,WAAU,wBACd;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,cAAW;AAAA,QACX,aAAY;AAAA,QACZ,WAAU;AAAA,QACV,kBAAkB,EAAE,OAAO,OAAU;AAAA,QACrC,MAAM,oBAAC,SAAM;AAAA,QACb,WAAU;AAAA,QACV,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAK;AAAA,QACL,SAAQ;AAAA;AAAA,IACV;AAAA,IACC,WAAW,oBAAC,cAAW,OAAO,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AAAA,IAEzE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
@@ -1,14 +1,5 @@
1
- export interface SeoButtonProps {
2
- collectionSlug: string;
3
- fields: Record<string, string>;
4
- extractContentPath: string | null;
5
- site: {
6
- name: string;
7
- baseUrl: string;
8
- faviconUrl: string;
9
- };
10
- supportedLocales: string[];
11
- }
12
- export declare function SeoButton({ collectionSlug, fields, site, supportedLocales }: SeoButtonProps): import("react/jsx-runtime").JSX.Element;
1
+ import type { SeoButtonProps } from "./SeoButtonInner";
2
+ export type { SeoButtonProps };
3
+ export declare function SeoButton(props: SeoButtonProps): import("react/jsx-runtime").JSX.Element | null;
13
4
  export default SeoButton;
14
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SeoButton/index.tsx"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAcD,wBAAgB,SAAS,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,cAAc,2CAuD3F;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SeoButton/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,kDAM9C;AAED,eAAe,SAAS,CAAC"}
@@ -1,77 +1,13 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { Button, useModal } from "@payloadcms/ui";
4
- import { cva } from "class-variance-authority";
5
- import { Gauge } from "lucide-react";
6
- import { useCallback, useState } from "react";
7
- import { SeoDrawer } from "../SeoDrawer";
8
- import { isKeyphrasePending } from "../SeoDrawer/keyphrasePending";
9
- import { useAnalysis } from "../SeoDrawer/useAnalysis";
10
- import { useLiveDocument } from "../SeoDrawer/useLiveDocument";
11
- const DRAWER_SLUG = "seo-analytics-drawer";
12
- const dotVariants = cva("absolute -top-[2px] -right-[2px] w-[8px] h-[8px] rounded-full border-[1.5px] border-[var(--theme-bg)] pointer-events-none", {
13
- variants: {
14
- status: {
15
- good: "bg-seo-good",
16
- warn: "bg-seo-warn",
17
- bad: "bg-seo-bad"
18
- }
19
- }
20
- });
21
- function SeoButton({ collectionSlug, fields, site, supportedLocales }) {
22
- const { openModal } = useModal();
23
- const [keyphrase, setKeyphrase] = useState("");
24
- const [activated, setActivated] = useState(false);
25
- const { signature, getInput, invalidateMedia } = useLiveDocument({
26
- collectionSlug,
27
- fields,
28
- site: { name: site.name, baseUrl: site.baseUrl },
29
- keyphrase,
30
- enabled: activated
31
- });
32
- const { result, analyzing, analyzedKeyphrase, analyzeNow } = useAnalysis({ getInput, signature, supportedLocales, enabled: activated });
33
- const keyphrasePending = isKeyphrasePending(keyphrase, analyzedKeyphrase);
34
- const status = result?.overall.status ?? null;
35
- const open = useCallback(() => {
36
- invalidateMedia();
37
- if (activated) {
38
- analyzeNow();
39
- } else {
40
- setActivated(true);
41
- }
42
- openModal(DRAWER_SLUG);
43
- }, [activated, analyzeNow, invalidateMedia, openModal]);
44
- return /* @__PURE__ */ jsxs("span", { className: "relative inline-flex", children: [
45
- /* @__PURE__ */ jsx(
46
- Button,
47
- {
48
- "aria-label": "SEO Analytics",
49
- buttonStyle: "none",
50
- className: "seo-doc-btn m-0 w-[calc(var(--base)*1.6)] h-[calc(var(--base)*1.6)] inline-flex items-center justify-center border border-[var(--theme-elevation-100)] rounded-rs bg-transparent text-neutral-800 transition-[border-color,background-color] duration-100 hover:border-neutral-300 hover:bg-neutral-100",
51
- extraButtonProps: { title: void 0 },
52
- icon: /* @__PURE__ */ jsx(Gauge, {}),
53
- iconStyle: "without-border",
54
- margin: false,
55
- onClick: open,
56
- size: "small",
57
- tooltip: "SEO Analytics"
58
- }
59
- ),
60
- status ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: dotVariants({ status }) }) : null,
61
- /* @__PURE__ */ jsx(
62
- SeoDrawer,
63
- {
64
- analyzeNow,
65
- analyzing,
66
- drawerSlug: DRAWER_SLUG,
67
- keyphrase,
68
- keyphrasePending,
69
- result,
70
- setKeyphrase,
71
- site
72
- }
73
- )
74
- ] });
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useDocumentInfo } from "@payloadcms/ui";
4
+ import { isExistingDocument } from "./isExistingDocument";
5
+ import { SeoButtonInner } from "./SeoButtonInner";
6
+ function SeoButton(props) {
7
+ const { id } = useDocumentInfo();
8
+ if (!isExistingDocument(id))
9
+ return null;
10
+ return /* @__PURE__ */ jsx(SeoButtonInner, { ...props });
75
11
  }
76
12
  var SeoButton_default = SeoButton;
77
13
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/SeoButton/index.tsx"],"sourcesContent":["\"use client\";\nimport { Button, useModal } from \"@payloadcms/ui\";\nimport { cva } from \"class-variance-authority\";\nimport { Gauge } from \"lucide-react\";\nimport { useCallback, useState } from \"react\";\nimport { SeoDrawer } from \"../SeoDrawer\";\nimport { isKeyphrasePending } from \"../SeoDrawer/keyphrasePending\";\nimport { useAnalysis } from \"../SeoDrawer/useAnalysis\";\nimport { useLiveDocument } from \"../SeoDrawer/useLiveDocument\";\n\nexport interface SeoButtonProps {\n collectionSlug: string;\n fields: Record<string, string>;\n extractContentPath: string | null;\n site: { name: string; baseUrl: string; faviconUrl: string };\n supportedLocales: string[];\n}\n\nconst DRAWER_SLUG = \"seo-analytics-drawer\";\n\nconst dotVariants = cva(\"absolute -top-[2px] -right-[2px] w-[8px] h-[8px] rounded-full border-[1.5px] border-[var(--theme-bg)] pointer-events-none\", {\n variants: {\n status: {\n good: \"bg-seo-good\",\n warn: \"bg-seo-warn\",\n bad: \"bg-seo-bad\",\n },\n },\n});\n\nexport function SeoButton({ collectionSlug, fields, site, supportedLocales }: SeoButtonProps) {\n const { openModal } = useModal();\n const [keyphrase, setKeyphrase] = useState(\"\");\n const [activated, setActivated] = useState(false);\n\n const { signature, getInput, invalidateMedia } = useLiveDocument({\n collectionSlug,\n fields,\n site: { name: site.name, baseUrl: site.baseUrl },\n keyphrase,\n enabled: activated,\n });\n const { result, analyzing, analyzedKeyphrase, analyzeNow } = useAnalysis({ getInput, signature, supportedLocales, enabled: activated });\n\n const keyphrasePending = isKeyphrasePending(keyphrase, analyzedKeyphrase);\n\n const status = result?.overall.status ?? null;\n\n const open = useCallback(() => {\n invalidateMedia();\n if (activated) {\n analyzeNow();\n } else {\n setActivated(true);\n }\n openModal(DRAWER_SLUG);\n }, [activated, analyzeNow, invalidateMedia, openModal]);\n\n return (\n <span className=\"relative inline-flex\">\n <Button\n aria-label=\"SEO Analytics\"\n buttonStyle=\"none\"\n className=\"seo-doc-btn m-0 w-[calc(var(--base)*1.6)] h-[calc(var(--base)*1.6)] inline-flex items-center justify-center border border-[var(--theme-elevation-100)] rounded-rs bg-transparent text-neutral-800 transition-[border-color,background-color] duration-100 hover:border-neutral-300 hover:bg-neutral-100\"\n extraButtonProps={{ title: undefined }}\n icon={<Gauge />}\n iconStyle=\"without-border\"\n margin={false}\n onClick={open}\n size=\"small\"\n tooltip=\"SEO Analytics\"\n />\n {status ? <span aria-hidden=\"true\" className={dotVariants({ status })} /> : null}\n <SeoDrawer\n analyzeNow={analyzeNow}\n analyzing={analyzing}\n drawerSlug={DRAWER_SLUG}\n keyphrase={keyphrase}\n keyphrasePending={keyphrasePending}\n result={result}\n setKeyphrase={setKeyphrase}\n site={site}\n />\n </span>\n );\n}\n\nexport default SeoButton;\n"],"mappings":";AA2DI,SAMU,KANV;AA1DJ,SAAS,QAAQ,gBAAgB;AACjC,SAAS,WAAW;AACpB,SAAS,aAAa;AACtB,SAAS,aAAa,gBAAgB;AACtC,SAAS,iBAAiB;AAC1B,SAAS,0BAA0B;AACnC,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAUhC,MAAM,cAAc;AAEpB,MAAM,cAAc,IAAI,6HAA6H;AAAA,EACnJ,UAAU;AAAA,IACR,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK;AAAA,IACP;AAAA,EACF;AACF,CAAC;AAEM,SAAS,UAAU,EAAE,gBAAgB,QAAQ,MAAM,iBAAiB,GAAmB;AAC5F,QAAM,EAAE,UAAU,IAAI,SAAS;AAC/B,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,EAAE;AAC7C,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,QAAM,EAAE,WAAW,UAAU,gBAAgB,IAAI,gBAAgB;AAAA,IAC/D;AAAA,IACA;AAAA,IACA,MAAM,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,QAAQ;AAAA,IAC/C;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AACD,QAAM,EAAE,QAAQ,WAAW,mBAAmB,WAAW,IAAI,YAAY,EAAE,UAAU,WAAW,kBAAkB,SAAS,UAAU,CAAC;AAEtI,QAAM,mBAAmB,mBAAmB,WAAW,iBAAiB;AAExE,QAAM,SAAS,QAAQ,QAAQ,UAAU;AAEzC,QAAM,OAAO,YAAY,MAAM;AAC7B,oBAAgB;AAChB,QAAI,WAAW;AACb,iBAAW;AAAA,IACb,OAAO;AACL,mBAAa,IAAI;AAAA,IACnB;AACA,cAAU,WAAW;AAAA,EACvB,GAAG,CAAC,WAAW,YAAY,iBAAiB,SAAS,CAAC;AAEtD,SACE,qBAAC,UAAK,WAAU,wBACd;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,cAAW;AAAA,QACX,aAAY;AAAA,QACZ,WAAU;AAAA,QACV,kBAAkB,EAAE,OAAO,OAAU;AAAA,QACrC,MAAM,oBAAC,SAAM;AAAA,QACb,WAAU;AAAA,QACV,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAK;AAAA,QACL,SAAQ;AAAA;AAAA,IACV;AAAA,IACC,SAAS,oBAAC,UAAK,eAAY,QAAO,WAAW,YAAY,EAAE,OAAO,CAAC,GAAG,IAAK;AAAA,IAC5E;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,IAAO,oBAAQ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/SeoButton/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { useDocumentInfo } from \"@payloadcms/ui\";\nimport { isExistingDocument } from \"./isExistingDocument\";\nimport { SeoButtonInner } from \"./SeoButtonInner\";\nimport type { SeoButtonProps } from \"./SeoButtonInner\";\n\nexport type { SeoButtonProps };\n\nexport function SeoButton(props: SeoButtonProps) {\n const { id } = useDocumentInfo();\n\n if (!isExistingDocument(id)) return null;\n\n return <SeoButtonInner {...props} />;\n}\n\nexport default SeoButton;\n"],"mappings":";AAcS;AAZT,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,sBAAsB;AAKxB,SAAS,UAAU,OAAuB;AAC/C,QAAM,EAAE,GAAG,IAAI,gBAAgB;AAE/B,MAAI,CAAC,mBAAmB,EAAE;AAAG,WAAO;AAEpC,SAAO,oBAAC,kBAAgB,GAAG,OAAO;AACpC;AAEA,IAAO,oBAAQ;","names":[]}
@@ -0,0 +1,2 @@
1
+ export declare function isExistingDocument(id: string | number | null | undefined): boolean;
2
+ //# sourceMappingURL=isExistingDocument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isExistingDocument.d.ts","sourceRoot":"","sources":["../../../src/components/SeoButton/isExistingDocument.ts"],"names":[],"mappings":"AAAA,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAKlF"}
@@ -0,0 +1,11 @@
1
+ function isExistingDocument(id) {
2
+ if (id === null || id === void 0)
3
+ return false;
4
+ if (typeof id === "string")
5
+ return id.trim().length > 0;
6
+ return true;
7
+ }
8
+ export {
9
+ isExistingDocument
10
+ };
11
+ //# sourceMappingURL=isExistingDocument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/SeoButton/isExistingDocument.ts"],"sourcesContent":["export function isExistingDocument(id: string | number | null | undefined): boolean {\n if (id === null || id === undefined) return false;\n if (typeof id === \"string\") return id.trim().length > 0;\n\n return true;\n}\n"],"mappings":"AAAO,SAAS,mBAAmB,IAAiD;AAClF,MAAI,OAAO,QAAQ,OAAO;AAAW,WAAO;AAC5C,MAAI,OAAO,OAAO;AAAU,WAAO,GAAG,KAAK,EAAE,SAAS;AAEtD,SAAO;AACT;","names":[]}
@@ -0,0 +1,7 @@
1
+ import type { HeadingLevelCount } from "../../../../engine/types/analysis";
2
+ interface HeadingLevelTilesProps {
3
+ levels: HeadingLevelCount[];
4
+ }
5
+ export declare function HeadingLevelTiles({ levels }: HeadingLevelTilesProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=HeadingLevelTiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeadingLevelTiles.d.ts","sourceRoot":"","sources":["../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingLevelTiles.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAG3E,UAAU,sBAAsB;IAC9B,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAAE,sBAAsB,2CAqBnE"}
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { cn } from "../../../../utils/style";
4
+ function HeadingLevelTiles({ levels }) {
5
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-6 gap-[6px] px-[15px] pt-[8px] pb-[12px]", children: levels.map(({ level, count }) => {
6
+ const zero = count === 0;
7
+ return /* @__PURE__ */ jsxs(
8
+ "div",
9
+ {
10
+ className: cn(
11
+ "flex items-baseline justify-center gap-[5px] rounded-rs border px-[2px] py-[7px]",
12
+ zero ? "border-dashed border-neutral-300 bg-neutral-50" : "border-neutral-200 bg-neutral-0"
13
+ ),
14
+ children: [
15
+ /* @__PURE__ */ jsxs("span", { className: cn("font-mono text-[12px] font-semibold", zero ? "text-neutral-400" : "text-neutral-500"), children: [
16
+ "H",
17
+ level
18
+ ] }),
19
+ /* @__PURE__ */ jsx("span", { className: cn("font-mono text-[12px] font-bold", zero ? "text-neutral-300" : "text-neutral-1000"), children: count })
20
+ ]
21
+ },
22
+ level
23
+ );
24
+ }) });
25
+ }
26
+ export {
27
+ HeadingLevelTiles
28
+ };
29
+ //# sourceMappingURL=HeadingLevelTiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingLevelTiles.tsx"],"sourcesContent":["\"use client\";\n\nimport type { HeadingLevelCount } from \"../../../../engine/types/analysis\";\nimport { cn } from \"../../../../utils/style\";\n\ninterface HeadingLevelTilesProps {\n levels: HeadingLevelCount[];\n}\n\nexport function HeadingLevelTiles({ levels }: HeadingLevelTilesProps) {\n return (\n <div className=\"grid grid-cols-6 gap-[6px] px-[15px] pt-[8px] pb-[12px]\">\n {levels.map(({ level, count }) => {\n const zero = count === 0;\n\n return (\n <div\n key={level}\n className={cn(\n \"flex items-baseline justify-center gap-[5px] rounded-rs border px-[2px] py-[7px]\",\n zero ? \"border-dashed border-neutral-300 bg-neutral-50\" : \"border-neutral-200 bg-neutral-0\"\n )}\n >\n <span className={cn(\"font-mono text-[12px] font-semibold\", zero ? \"text-neutral-400\" : \"text-neutral-500\")}>H{level}</span>\n <span className={cn(\"font-mono text-[12px] font-bold\", zero ? \"text-neutral-300\" : \"text-neutral-1000\")}>{count}</span>\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";AAuBY,SACA,KADA;AApBZ,SAAS,UAAU;AAMZ,SAAS,kBAAkB,EAAE,OAAO,GAA2B;AACpE,SACE,oBAAC,SAAI,WAAU,2DACZ,iBAAO,IAAI,CAAC,EAAE,OAAO,MAAM,MAAM;AAChC,UAAM,OAAO,UAAU;AAEvB,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,WAAW;AAAA,UACT;AAAA,UACA,OAAO,mDAAmD;AAAA,QAC5D;AAAA,QAEA;AAAA,+BAAC,UAAK,WAAW,GAAG,uCAAuC,OAAO,qBAAqB,kBAAkB,GAAG;AAAA;AAAA,YAAE;AAAA,aAAM;AAAA,UACpH,oBAAC,UAAK,WAAW,GAAG,mCAAmC,OAAO,qBAAqB,mBAAmB,GAAI,iBAAM;AAAA;AAAA;AAAA,MAP3G;AAAA,IAQP;AAAA,EAEJ,CAAC,GACH;AAEJ;","names":[]}
@@ -0,0 +1,4 @@
1
+ export declare function Chevron({ open }: {
2
+ open: boolean;
3
+ }): import("react/jsx-runtime").JSX.Element;
4
+ //# sourceMappingURL=Chevron.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Chevron.d.ts","sourceRoot":"","sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/Chevron.tsx"],"names":[],"mappings":"AAGA,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,2CAElD"}
@@ -0,0 +1,10 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { ChevronDown } from "lucide-react";
3
+ import { cn } from "../../../../../utils/style";
4
+ function Chevron({ open }) {
5
+ return /* @__PURE__ */ jsx(ChevronDown, { size: 13, strokeWidth: 2.2, "aria-hidden": "true", className: cn("flex-none text-neutral-500 transition-transform duration-150", !open && "-rotate-90") });
6
+ }
7
+ export {
8
+ Chevron
9
+ };
10
+ //# sourceMappingURL=Chevron.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/Chevron.tsx"],"sourcesContent":["import { ChevronDown } from \"lucide-react\";\nimport { cn } from \"../../../../../utils/style\";\n\nexport function Chevron({ open }: { open: boolean }) {\n return <ChevronDown size={13} strokeWidth={2.2} aria-hidden=\"true\" className={cn(\"flex-none text-neutral-500 transition-transform duration-150\", !open && \"-rotate-90\")} />;\n}\n"],"mappings":"AAIS;AAJT,SAAS,mBAAmB;AAC5B,SAAS,UAAU;AAEZ,SAAS,QAAQ,EAAE,KAAK,GAAsB;AACnD,SAAO,oBAAC,eAAY,MAAM,IAAI,aAAa,KAAK,eAAY,QAAO,WAAW,GAAG,gEAAgE,CAAC,QAAQ,YAAY,GAAG;AAC3K;","names":[]}
@@ -0,0 +1,10 @@
1
+ import type { HeadingNode } from "../../../../../engine/types/analysis";
2
+ interface HeadingTreeGroupProps {
3
+ nodes: HeadingNode[];
4
+ depth: number;
5
+ collapsed: ReadonlySet<string>;
6
+ onToggle: (id: string) => void;
7
+ }
8
+ export declare function HeadingTreeGroup({ nodes, depth, collapsed, onToggle }: HeadingTreeGroupProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=HeadingTreeGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeadingTreeGroup.d.ts","sourceRoot":"","sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeGroup.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAgExE,UAAU,qBAAqB;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,qBAAqB,2CAQ5F"}
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { HeadingTreeRow } from "./HeadingTreeRow";
4
+ import { useHeadingRails } from "./useHeadingRails";
5
+ function HeadingTreeNode({ node, depth, globalFirst, collapsed, onToggle, onBadgeMount }) {
6
+ const hasKids = node.children.length > 0;
7
+ const isOpen = hasKids && !collapsed.has(node.id);
8
+ const { containerRef, setBadgeRef, registerChildBadge, rails } = useHeadingRails({
9
+ node,
10
+ isOpen,
11
+ collapsed,
12
+ onBadgeMount
13
+ });
14
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
15
+ rails ? /* @__PURE__ */ jsxs(Fragment, { children: [
16
+ /* @__PURE__ */ jsx(
17
+ "span",
18
+ {
19
+ className: "absolute w-[1.1px] bg-neutral-150",
20
+ style: {
21
+ left: rails.vertical.left,
22
+ top: rails.vertical.top,
23
+ height: rails.vertical.height
24
+ }
25
+ }
26
+ ),
27
+ rails.elbows.map((elbow) => /* @__PURE__ */ jsx(
28
+ "span",
29
+ {
30
+ className: "absolute h-[1.1px] bg-neutral-150",
31
+ style: {
32
+ left: elbow.left,
33
+ top: elbow.top,
34
+ width: elbow.width
35
+ }
36
+ },
37
+ elbow.id
38
+ ))
39
+ ] }) : null,
40
+ /* @__PURE__ */ jsx(HeadingTreeRow, { node, depth, hasKids, isOpen, globalFirst, onToggle, badgeRef: setBadgeRef }),
41
+ isOpen ? /* @__PURE__ */ jsx("div", { children: node.children.map((child) => /* @__PURE__ */ jsx(HeadingTreeNode, { node: child, depth: depth + 1, globalFirst: false, collapsed, onToggle, onBadgeMount: registerChildBadge(child.id) }, child.id)) }) : null
42
+ ] });
43
+ }
44
+ function HeadingTreeGroup({ nodes, depth, collapsed, onToggle }) {
45
+ return /* @__PURE__ */ jsx(Fragment, { children: nodes.map((node, i) => /* @__PURE__ */ jsx(HeadingTreeNode, { node, depth, globalFirst: depth === 0 && i === 0, collapsed, onToggle }, node.id)) });
46
+ }
47
+ export {
48
+ HeadingTreeGroup
49
+ };
50
+ //# sourceMappingURL=HeadingTreeGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeGroup.tsx"],"sourcesContent":["\"use client\";\n\nimport type { HeadingNode } from \"../../../../../engine/types/analysis\";\nimport { HeadingTreeRow } from \"./HeadingTreeRow\";\nimport { useHeadingRails } from \"./useHeadingRails\";\n\ninterface HeadingTreeNodeProps {\n node: HeadingNode;\n depth: number;\n globalFirst: boolean;\n collapsed: ReadonlySet<string>;\n onToggle: (id: string) => void;\n onBadgeMount?: (el: HTMLSpanElement | null) => void;\n}\n\nfunction HeadingTreeNode({ node, depth, globalFirst, collapsed, onToggle, onBadgeMount }: HeadingTreeNodeProps) {\n const hasKids = node.children.length > 0;\n const isOpen = hasKids && !collapsed.has(node.id);\n\n const { containerRef, setBadgeRef, registerChildBadge, rails } = useHeadingRails({\n node,\n isOpen,\n collapsed,\n onBadgeMount,\n });\n\n return (\n <div ref={containerRef} className=\"relative\">\n {rails ? (\n <>\n <span\n className=\"absolute w-[1.1px] bg-neutral-150\"\n style={{\n left: rails.vertical.left,\n top: rails.vertical.top,\n height: rails.vertical.height,\n }}\n />\n\n {rails.elbows.map((elbow) => (\n <span\n key={elbow.id}\n className=\"absolute h-[1.1px] bg-neutral-150\"\n style={{\n left: elbow.left,\n top: elbow.top,\n width: elbow.width,\n }}\n />\n ))}\n </>\n ) : null}\n\n <HeadingTreeRow node={node} depth={depth} hasKids={hasKids} isOpen={isOpen} globalFirst={globalFirst} onToggle={onToggle} badgeRef={setBadgeRef} />\n\n {isOpen ? (\n <div>\n {node.children.map((child) => (\n <HeadingTreeNode key={child.id} node={child} depth={depth + 1} globalFirst={false} collapsed={collapsed} onToggle={onToggle} onBadgeMount={registerChildBadge(child.id)} />\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n\ninterface HeadingTreeGroupProps {\n nodes: HeadingNode[];\n depth: number;\n collapsed: ReadonlySet<string>;\n onToggle: (id: string) => void;\n}\n\nexport function HeadingTreeGroup({ nodes, depth, collapsed, onToggle }: HeadingTreeGroupProps) {\n return (\n <>\n {nodes.map((node, i) => (\n <HeadingTreeNode key={node.id} node={node} depth={depth} globalFirst={depth === 0 && i === 0} collapsed={collapsed} onToggle={onToggle} />\n ))}\n </>\n );\n}\n"],"mappings":";AA6BQ,mBACE,KADF;AA1BR,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAWhC,SAAS,gBAAgB,EAAE,MAAM,OAAO,aAAa,WAAW,UAAU,aAAa,GAAyB;AAC9G,QAAM,UAAU,KAAK,SAAS,SAAS;AACvC,QAAM,SAAS,WAAW,CAAC,UAAU,IAAI,KAAK,EAAE;AAEhD,QAAM,EAAE,cAAc,aAAa,oBAAoB,MAAM,IAAI,gBAAgB;AAAA,IAC/E;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE,qBAAC,SAAI,KAAK,cAAc,WAAU,YAC/B;AAAA,YACC,iCACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO;AAAA,YACL,MAAM,MAAM,SAAS;AAAA,YACrB,KAAK,MAAM,SAAS;AAAA,YACpB,QAAQ,MAAM,SAAS;AAAA,UACzB;AAAA;AAAA,MACF;AAAA,MAEC,MAAM,OAAO,IAAI,CAAC,UACjB;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UACV,OAAO;AAAA,YACL,MAAM,MAAM;AAAA,YACZ,KAAK,MAAM;AAAA,YACX,OAAO,MAAM;AAAA,UACf;AAAA;AAAA,QANK,MAAM;AAAA,MAOb,CACD;AAAA,OACH,IACE;AAAA,IAEJ,oBAAC,kBAAe,MAAY,OAAc,SAAkB,QAAgB,aAA0B,UAAoB,UAAU,aAAa;AAAA,IAEhJ,SACC,oBAAC,SACE,eAAK,SAAS,IAAI,CAAC,UAClB,oBAAC,mBAA+B,MAAM,OAAO,OAAO,QAAQ,GAAG,aAAa,OAAO,WAAsB,UAAoB,cAAc,mBAAmB,MAAM,EAAE,KAAhJ,MAAM,EAA6I,CAC1K,GACH,IACE;AAAA,KACN;AAEJ;AASO,SAAS,iBAAiB,EAAE,OAAO,OAAO,WAAW,SAAS,GAA0B;AAC7F,SACE,gCACG,gBAAM,IAAI,CAAC,MAAM,MAChB,oBAAC,mBAA8B,MAAY,OAAc,aAAa,UAAU,KAAK,MAAM,GAAG,WAAsB,YAA9F,KAAK,EAA6G,CACzI,GACH;AAEJ;","names":[]}
@@ -0,0 +1,15 @@
1
+ import type { Ref } from "react";
2
+ import type { HeadingNode } from "../../../../../engine/types/analysis";
3
+ interface HeadingTreeRowProps {
4
+ node: HeadingNode;
5
+ depth: number;
6
+ hasKids: boolean;
7
+ isOpen: boolean;
8
+ globalFirst: boolean;
9
+ onToggle: (id: string) => void;
10
+ rowRef?: Ref<HTMLDivElement>;
11
+ badgeRef?: Ref<HTMLSpanElement>;
12
+ }
13
+ export declare function HeadingTreeRow({ node, depth, hasKids, isOpen, globalFirst, onToggle, rowRef, badgeRef }: HeadingTreeRowProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
15
+ //# sourceMappingURL=HeadingTreeRow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeadingTreeRow.d.ts","sourceRoot":"","sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAIxE,UAAU,mBAAmB;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;CACjC;AAED,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CAuC5H"}
@@ -0,0 +1,38 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../../../../utils/style";
3
+ import { Chevron } from "./Chevron";
4
+ function HeadingTreeRow({ node, depth, hasKids, isOpen, globalFirst, onToggle, rowRef, badgeRef }) {
5
+ return /* @__PURE__ */ jsxs(
6
+ "div",
7
+ {
8
+ ref: rowRef,
9
+ className: cn("relative flex items-center gap-[9px] h-[34px] box-border", hasKids && "cursor-pointer focus-visible:outline-2 focus-visible:outline-neutral-400 focus-visible:-outline-offset-2"),
10
+ style: { paddingLeft: depth * 20 },
11
+ role: hasKids ? "button" : void 0,
12
+ tabIndex: hasKids ? 0 : void 0,
13
+ "aria-expanded": hasKids ? isOpen : void 0,
14
+ onClick: hasKids ? () => onToggle(node.id) : void 0,
15
+ onKeyDown: hasKids ? (e) => {
16
+ if (e.key === "Enter" || e.key === " ") {
17
+ e.preventDefault();
18
+ onToggle(node.id);
19
+ }
20
+ } : void 0,
21
+ title: hasKids ? isOpen ? "Collapse" : "Expand" : void 0,
22
+ children: [
23
+ /* @__PURE__ */ jsxs("span", { ref: badgeRef, className: "flex-none font-mono text-[9px] font-bold leading-[100%] text-neutral-1000 bg-neutral-150 rounded-rs px-[6px] py-[3px] min-w-[22px] text-center", children: [
24
+ "H",
25
+ node.level
26
+ ] }),
27
+ /* @__PURE__ */ jsxs("div", { className: cn("flex-1 min-w-0 flex items-center gap-[9px] h-full", !globalFirst && "border-t border-neutral-150"), children: [
28
+ node.text ? /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 truncate text-[12px] font-medium text-neutral-800", title: node.text, children: node.text }) : /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 truncate text-[12px] font-medium italic text-neutral-400", children: "(empty heading)" }),
29
+ hasKids ? /* @__PURE__ */ jsx(Chevron, { open: isOpen }) : null
30
+ ] })
31
+ ]
32
+ }
33
+ );
34
+ }
35
+ export {
36
+ HeadingTreeRow
37
+ };
38
+ //# sourceMappingURL=HeadingTreeRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/HeadingTreeRow.tsx"],"sourcesContent":["import type { Ref } from \"react\";\nimport type { HeadingNode } from \"../../../../../engine/types/analysis\";\nimport { cn } from \"../../../../../utils/style\";\nimport { Chevron } from \"./Chevron\";\n\ninterface HeadingTreeRowProps {\n node: HeadingNode;\n depth: number;\n hasKids: boolean;\n isOpen: boolean;\n globalFirst: boolean;\n onToggle: (id: string) => void;\n rowRef?: Ref<HTMLDivElement>;\n badgeRef?: Ref<HTMLSpanElement>;\n}\n\nexport function HeadingTreeRow({ node, depth, hasKids, isOpen, globalFirst, onToggle, rowRef, badgeRef }: HeadingTreeRowProps) {\n return (\n <div\n ref={rowRef}\n className={cn(\"relative flex items-center gap-[9px] h-[34px] box-border\", hasKids && \"cursor-pointer focus-visible:outline-2 focus-visible:outline-neutral-400 focus-visible:-outline-offset-2\")}\n style={{ paddingLeft: depth * 20 }}\n role={hasKids ? \"button\" : undefined}\n tabIndex={hasKids ? 0 : undefined}\n aria-expanded={hasKids ? isOpen : undefined}\n onClick={hasKids ? () => onToggle(node.id) : undefined}\n onKeyDown={\n hasKids\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onToggle(node.id);\n }\n }\n : undefined\n }\n title={hasKids ? (isOpen ? \"Collapse\" : \"Expand\") : undefined}\n >\n <span ref={badgeRef} className=\"flex-none font-mono text-[9px] font-bold leading-[100%] text-neutral-1000 bg-neutral-150 rounded-rs px-[6px] py-[3px] min-w-[22px] text-center\">\n H{node.level}\n </span>\n\n <div className={cn(\"flex-1 min-w-0 flex items-center gap-[9px] h-full\", !globalFirst && \"border-t border-neutral-150\")}>\n {node.text ? (\n <span className=\"flex-1 min-w-0 truncate text-[12px] font-medium text-neutral-800\" title={node.text}>\n {node.text}\n </span>\n ) : (\n <span className=\"flex-1 min-w-0 truncate text-[12px] font-medium italic text-neutral-400\">(empty heading)</span>\n )}\n\n {hasKids ? <Chevron open={isOpen} /> : null}\n </div>\n </div>\n );\n}\n"],"mappings":"AAsCM,SAMI,KANJ;AApCN,SAAS,UAAU;AACnB,SAAS,eAAe;AAajB,SAAS,eAAe,EAAE,MAAM,OAAO,SAAS,QAAQ,aAAa,UAAU,QAAQ,SAAS,GAAwB;AAC7H,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW,GAAG,4DAA4D,WAAW,0GAA0G;AAAA,MAC/L,OAAO,EAAE,aAAa,QAAQ,GAAG;AAAA,MACjC,MAAM,UAAU,WAAW;AAAA,MAC3B,UAAU,UAAU,IAAI;AAAA,MACxB,iBAAe,UAAU,SAAS;AAAA,MAClC,SAAS,UAAU,MAAM,SAAS,KAAK,EAAE,IAAI;AAAA,MAC7C,WACE,UACI,CAAC,MAAM;AACL,YAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,YAAE,eAAe;AACjB,mBAAS,KAAK,EAAE;AAAA,QAClB;AAAA,MACF,IACA;AAAA,MAEN,OAAO,UAAW,SAAS,aAAa,WAAY;AAAA,MAEpD;AAAA,6BAAC,UAAK,KAAK,UAAU,WAAU,kJAAiJ;AAAA;AAAA,UAC5K,KAAK;AAAA,WACT;AAAA,QAEA,qBAAC,SAAI,WAAW,GAAG,qDAAqD,CAAC,eAAe,6BAA6B,GAClH;AAAA,eAAK,OACJ,oBAAC,UAAK,WAAU,oEAAmE,OAAO,KAAK,MAC5F,eAAK,MACR,IAEA,oBAAC,UAAK,WAAU,2EAA0E,6BAAe;AAAA,UAG1G,UAAU,oBAAC,WAAQ,MAAM,QAAQ,IAAK;AAAA,WACzC;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -0,0 +1,3 @@
1
+ import type { HeadingNode } from "../../../../../engine/types/analysis";
2
+ export declare function collectParentIds(nodes: HeadingNode[]): string[];
3
+ //# sourceMappingURL=headingTreeView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headingTreeView.d.ts","sourceRoot":"","sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAExE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,EAAE,CAe/D"}
@@ -0,0 +1,17 @@
1
+ function collectParentIds(nodes) {
2
+ const ids = [];
3
+ const walk = (ns) => {
4
+ for (const n of ns) {
5
+ if (n.children.length > 0) {
6
+ ids.push(n.id);
7
+ walk(n.children);
8
+ }
9
+ }
10
+ };
11
+ walk(nodes);
12
+ return ids;
13
+ }
14
+ export {
15
+ collectParentIds
16
+ };
17
+ //# sourceMappingURL=headingTreeView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/headingTreeView.ts"],"sourcesContent":["import type { HeadingNode } from \"../../../../../engine/types/analysis\";\n\nexport function collectParentIds(nodes: HeadingNode[]): string[] {\n const ids: string[] = [];\n\n const walk = (ns: HeadingNode[]) => {\n for (const n of ns) {\n if (n.children.length > 0) {\n ids.push(n.id);\n walk(n.children);\n }\n }\n };\n\n walk(nodes);\n\n return ids;\n}\n"],"mappings":"AAEO,SAAS,iBAAiB,OAAgC;AAC/D,QAAM,MAAgB,CAAC;AAEvB,QAAM,OAAO,CAAC,OAAsB;AAClC,eAAW,KAAK,IAAI;AAClB,UAAI,EAAE,SAAS,SAAS,GAAG;AACzB,YAAI,KAAK,EAAE,EAAE;AACb,aAAK,EAAE,QAAQ;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,OAAK,KAAK;AAEV,SAAO;AACT;","names":[]}
@@ -0,0 +1,6 @@
1
+ import type { HeadingNode } from "../../../../../engine/types/analysis";
2
+ export interface HeadingTreeProps {
3
+ tree: HeadingNode[];
4
+ }
5
+ export declare function HeadingTree({ tree }: HeadingTreeProps): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAKxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,WAAW,EAAE,CAAC;CACrB;AAID,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useState } from "react";
4
+ import { cn } from "../../../../../utils/style";
5
+ import { collectParentIds } from "./headingTreeView";
6
+ import { HeadingTreeGroup } from "./HeadingTreeGroup";
7
+ const SUB_LABEL = "text-[9.5px] font-semibold uppercase tracking-[0.05em] text-neutral-500";
8
+ function HeadingTree({ tree }) {
9
+ const parentIds = useMemo(() => collectParentIds(tree), [tree]);
10
+ const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
11
+ const empty = tree.length === 0;
12
+ const allCollapsed = parentIds.length > 0 && parentIds.every((id) => collapsed.has(id));
13
+ const toggleAll = () => setCollapsed(allCollapsed ? /* @__PURE__ */ new Set() : new Set(parentIds));
14
+ const toggle = (id) => setCollapsed((prev) => {
15
+ const next = new Set(prev);
16
+ if (next.has(id))
17
+ next.delete(id);
18
+ else
19
+ next.add(id);
20
+ return next;
21
+ });
22
+ return /* @__PURE__ */ jsxs("div", { className: "px-[15px] pt-[11px] border-t border-neutral-150", children: [
23
+ /* @__PURE__ */ jsxs("div", { className: cn(SUB_LABEL, "flex items-center justify-between"), children: [
24
+ /* @__PURE__ */ jsx("span", { children: "Structure" }),
25
+ !empty && /* @__PURE__ */ jsx(
26
+ "button",
27
+ {
28
+ type: "button",
29
+ onClick: toggleAll,
30
+ className: "text-[11px] font-medium normal-case tracking-normal text-neutral-600 hover:text-neutral-1000 hover:underline underline-offset-2 bg-transparent border-0 p-0 cursor-pointer",
31
+ children: allCollapsed ? "Expand all" : "Collapse all"
32
+ }
33
+ )
34
+ ] }),
35
+ empty ? /* @__PURE__ */ jsx("div", { className: "px-[15px] pt-[20px] pb-[24px] text-center text-[12px] text-neutral-500", children: "No headings found in this content." }) : /* @__PURE__ */ jsx("div", { className: "relative pt-[5px] pb-[7px]", children: /* @__PURE__ */ jsx(HeadingTreeGroup, { nodes: tree, depth: 0, collapsed, onToggle: toggle }) })
36
+ ] });
37
+ }
38
+ export {
39
+ HeadingTree
40
+ };
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport type { HeadingNode } from \"../../../../../engine/types/analysis\";\nimport { cn } from \"../../../../../utils/style\";\nimport { collectParentIds } from \"./headingTreeView\";\nimport { HeadingTreeGroup } from \"./HeadingTreeGroup\";\n\nexport interface HeadingTreeProps {\n tree: HeadingNode[];\n}\n\nconst SUB_LABEL = \"text-[9.5px] font-semibold uppercase tracking-[0.05em] text-neutral-500\";\n\nexport function HeadingTree({ tree }: HeadingTreeProps) {\n const parentIds = useMemo(() => collectParentIds(tree), [tree]);\n const [collapsed, setCollapsed] = useState<Set<string>>(() => new Set());\n\n const empty = tree.length === 0;\n const allCollapsed = parentIds.length > 0 && parentIds.every((id) => collapsed.has(id));\n\n const toggleAll = () => setCollapsed(allCollapsed ? new Set() : new Set(parentIds));\n const toggle = (id: string) =>\n setCollapsed((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n\n return (\n <div className=\"px-[15px] pt-[11px] border-t border-neutral-150\">\n <div className={cn(SUB_LABEL, \"flex items-center justify-between\")}>\n <span>Structure</span>\n\n {!empty && (\n <button\n type=\"button\"\n onClick={toggleAll}\n className=\"text-[11px] font-medium normal-case tracking-normal text-neutral-600 hover:text-neutral-1000 hover:underline underline-offset-2 bg-transparent border-0 p-0 cursor-pointer\"\n >\n {allCollapsed ? \"Expand all\" : \"Collapse all\"}\n </button>\n )}\n </div>\n {empty ? (\n <div className=\"px-[15px] pt-[20px] pb-[24px] text-center text-[12px] text-neutral-500\">No headings found in this content.</div>\n ) : (\n <div className=\"relative pt-[5px] pb-[7px]\">\n <HeadingTreeGroup nodes={tree} depth={0} collapsed={collapsed} onToggle={toggle} />\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";AAgCM,SACE,KADF;AA9BN,SAAS,SAAS,gBAAgB;AAElC,SAAS,UAAU;AACnB,SAAS,wBAAwB;AACjC,SAAS,wBAAwB;AAMjC,MAAM,YAAY;AAEX,SAAS,YAAY,EAAE,KAAK,GAAqB;AACtD,QAAM,YAAY,QAAQ,MAAM,iBAAiB,IAAI,GAAG,CAAC,IAAI,CAAC;AAC9D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAsB,MAAM,oBAAI,IAAI,CAAC;AAEvE,QAAM,QAAQ,KAAK,WAAW;AAC9B,QAAM,eAAe,UAAU,SAAS,KAAK,UAAU,MAAM,CAAC,OAAO,UAAU,IAAI,EAAE,CAAC;AAEtF,QAAM,YAAY,MAAM,aAAa,eAAe,oBAAI,IAAI,IAAI,IAAI,IAAI,SAAS,CAAC;AAClF,QAAM,SAAS,CAAC,OACd,aAAa,CAAC,SAAS;AACrB,UAAM,OAAO,IAAI,IAAI,IAAI;AACzB,QAAI,KAAK,IAAI,EAAE;AAAG,WAAK,OAAO,EAAE;AAAA;AAC3B,WAAK,IAAI,EAAE;AAChB,WAAO;AAAA,EACT,CAAC;AAEH,SACE,qBAAC,SAAI,WAAU,mDACb;AAAA,yBAAC,SAAI,WAAW,GAAG,WAAW,mCAAmC,GAC/D;AAAA,0BAAC,UAAK,uBAAS;AAAA,MAEd,CAAC,SACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAU;AAAA,UAET,yBAAe,eAAe;AAAA;AAAA,MACjC;AAAA,OAEJ;AAAA,IACC,QACC,oBAAC,SAAI,WAAU,0EAAyE,gDAAkC,IAE1H,oBAAC,SAAI,WAAU,8BACb,8BAAC,oBAAiB,OAAO,MAAM,OAAO,GAAG,WAAsB,UAAU,QAAQ,GACnF;AAAA,KAEJ;AAEJ;","names":[]}
@@ -0,0 +1,32 @@
1
+ import type { RefObject } from "react";
2
+ import type { HeadingNode } from "../../../../../engine/types/analysis";
3
+ interface VerticalRail {
4
+ left: number;
5
+ top: number;
6
+ height: number;
7
+ }
8
+ interface Elbow {
9
+ id: string;
10
+ left: number;
11
+ top: number;
12
+ width: number;
13
+ }
14
+ export interface RailGeometry {
15
+ vertical: VerticalRail;
16
+ elbows: Elbow[];
17
+ }
18
+ interface UseHeadingRailsParams {
19
+ node: HeadingNode;
20
+ isOpen: boolean;
21
+ collapsed: ReadonlySet<string>;
22
+ onBadgeMount?: (el: HTMLSpanElement | null) => void;
23
+ }
24
+ interface UseHeadingRailsResult {
25
+ containerRef: RefObject<HTMLDivElement | null>;
26
+ setBadgeRef: (el: HTMLSpanElement | null) => void;
27
+ registerChildBadge: (childId: string) => (el: HTMLSpanElement | null) => void;
28
+ rails: RailGeometry | null;
29
+ }
30
+ export declare function useHeadingRails({ node, isOpen, collapsed, onBadgeMount }: UseHeadingRailsParams): UseHeadingRailsResult;
31
+ export {};
32
+ //# sourceMappingURL=useHeadingRails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHeadingRails.d.ts","sourceRoot":"","sources":["../../../../../../src/components/SeoDrawer/components/HeadingsSection/HeadingTree/useHeadingRails.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAExE,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,KAAK;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,UAAU,qBAAqB;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;CACrD;AAED,UAAU,qBAAqB;IAC7B,YAAY,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC/C,WAAW,EAAE,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;IAClD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9E,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,qBAAqB,GAAG,qBAAqB,CA6FvH"}