@geenius/seo 0.1.0 → 0.3.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 (126) hide show
  1. package/package.json +16 -3
  2. package/packages/convex/dist/index.d.ts +56 -0
  3. package/packages/convex/dist/index.js +133 -0
  4. package/packages/convex/dist/index.js.map +1 -0
  5. package/packages/react/README.md +1 -1
  6. package/packages/react/dist/index.d.ts +156 -0
  7. package/packages/react/dist/index.js +567 -0
  8. package/packages/react/dist/index.js.map +1 -0
  9. package/packages/react-css/README.md +1 -1
  10. package/packages/react-css/dist/index.cjs +571 -0
  11. package/packages/react-css/dist/index.cjs.map +1 -0
  12. package/packages/react-css/{src/seo.css → dist/index.css} +7 -153
  13. package/packages/react-css/dist/index.css.map +1 -0
  14. package/packages/react-css/dist/index.d.cts +53 -0
  15. package/packages/react-css/dist/index.d.ts +53 -0
  16. package/packages/react-css/dist/index.js +539 -0
  17. package/packages/react-css/dist/index.js.map +1 -0
  18. package/packages/shared/README.md +1 -1
  19. package/packages/shared/dist/index.d.ts +262 -0
  20. package/packages/shared/dist/index.js +381 -0
  21. package/packages/shared/dist/index.js.map +1 -0
  22. package/packages/solidjs/README.md +1 -1
  23. package/packages/solidjs/dist/index.d.ts +133 -0
  24. package/packages/solidjs/dist/index.js +416 -0
  25. package/packages/solidjs/dist/index.js.map +1 -0
  26. package/packages/solidjs-css/README.md +1 -1
  27. package/packages/solidjs-css/dist/index.cjs +399 -0
  28. package/packages/solidjs-css/dist/index.cjs.map +1 -0
  29. package/packages/solidjs-css/{src/seo.css → dist/index.css} +7 -153
  30. package/packages/solidjs-css/dist/index.css.map +1 -0
  31. package/packages/solidjs-css/dist/index.d.cts +53 -0
  32. package/packages/solidjs-css/dist/index.d.ts +53 -0
  33. package/packages/solidjs-css/dist/index.js +367 -0
  34. package/packages/solidjs-css/dist/index.js.map +1 -0
  35. package/.changeset/config.json +0 -11
  36. package/.github/CODEOWNERS +0 -1
  37. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
  38. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
  39. package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
  40. package/.github/dependabot.yml +0 -11
  41. package/.github/workflows/ci.yml +0 -23
  42. package/.github/workflows/release.yml +0 -29
  43. package/.nvmrc +0 -1
  44. package/.project/ACCOUNT.yaml +0 -4
  45. package/.project/IDEAS.yaml +0 -7
  46. package/.project/PROJECT.yaml +0 -11
  47. package/.project/ROADMAP.yaml +0 -15
  48. package/CODE_OF_CONDUCT.md +0 -16
  49. package/CONTRIBUTING.md +0 -26
  50. package/SECURITY.md +0 -15
  51. package/SUPPORT.md +0 -8
  52. package/packages/convex/package.json +0 -42
  53. package/packages/convex/src/functions.ts +0 -5
  54. package/packages/convex/src/mutations.ts +0 -83
  55. package/packages/convex/src/queries.ts +0 -57
  56. package/packages/convex/src/schema.ts +0 -23
  57. package/packages/convex/tsconfig.json +0 -19
  58. package/packages/convex/tsup.config.ts +0 -18
  59. package/packages/react/package.json +0 -49
  60. package/packages/react/src/components/ArticleJsonLd.tsx +0 -42
  61. package/packages/react/src/components/BreadcrumbsJsonLd.tsx +0 -24
  62. package/packages/react/src/components/MetaEditor.tsx +0 -147
  63. package/packages/react/src/components/SEOHead.tsx +0 -107
  64. package/packages/react/src/components/SEOPreview.tsx +0 -42
  65. package/packages/react/src/components/SEOScoreCard.tsx +0 -51
  66. package/packages/react/src/components/SitemapViewer.tsx +0 -36
  67. package/packages/react/src/components/index.ts +0 -7
  68. package/packages/react/src/hooks/index.ts +0 -4
  69. package/packages/react/src/hooks/useSEO.ts +0 -27
  70. package/packages/react/src/hooks/useSEOAdmin.ts +0 -42
  71. package/packages/react/src/hooks/useSEOScore.ts +0 -7
  72. package/packages/react/src/hooks/useSitemap.ts +0 -8
  73. package/packages/react/src/index.ts +0 -51
  74. package/packages/react/src/index.tsx +0 -11
  75. package/packages/react/src/pages/SEOAdminPage.tsx +0 -101
  76. package/packages/react/src/pages/SEOAnalyticsPage.tsx +0 -96
  77. package/packages/react/src/pages/index.ts +0 -2
  78. package/packages/react/tsconfig.json +0 -19
  79. package/packages/react/tsup.config.ts +0 -12
  80. package/packages/react-css/package.json +0 -36
  81. package/packages/react-css/src/components/ArticleJsonLd.tsx +0 -42
  82. package/packages/react-css/src/components/BreadcrumbsJsonLd.tsx +0 -24
  83. package/packages/react-css/src/components/MetaEditor.tsx +0 -147
  84. package/packages/react-css/src/components/SEOHead.tsx +0 -95
  85. package/packages/react-css/src/components/SEOPreview.tsx +0 -42
  86. package/packages/react-css/src/components/SEOScoreCard.tsx +0 -42
  87. package/packages/react-css/src/components/SitemapViewer.tsx +0 -36
  88. package/packages/react-css/src/components/index.ts +0 -7
  89. package/packages/react-css/src/index.ts +0 -9
  90. package/packages/react-css/src/pages/SEOAdminPage.tsx +0 -88
  91. package/packages/react-css/src/pages/SEOAnalyticsPage.tsx +0 -82
  92. package/packages/react-css/src/pages/index.ts +0 -2
  93. package/packages/react-css/tsup.config.ts +0 -2
  94. package/packages/shared/package.json +0 -42
  95. package/packages/shared/src/__tests__/seo.test.ts +0 -70
  96. package/packages/shared/src/config.ts +0 -297
  97. package/packages/shared/src/index.ts +0 -207
  98. package/packages/shared/tsconfig.json +0 -18
  99. package/packages/shared/tsup.config.ts +0 -11
  100. package/packages/shared/vitest.config.ts +0 -4
  101. package/packages/solidjs/package.json +0 -45
  102. package/packages/solidjs/src/components/ArticleJsonLd.tsx +0 -35
  103. package/packages/solidjs/src/components/BreadcrumbsJsonLd.tsx +0 -24
  104. package/packages/solidjs/src/components/MetaEditor.tsx +0 -155
  105. package/packages/solidjs/src/components/SEOHead.tsx +0 -109
  106. package/packages/solidjs/src/components/SEOPreview.tsx +0 -42
  107. package/packages/solidjs/src/components/SEOScoreCard.tsx +0 -57
  108. package/packages/solidjs/src/components/SitemapViewer.tsx +0 -44
  109. package/packages/solidjs/src/components/index.ts +0 -7
  110. package/packages/solidjs/src/index.ts +0 -11
  111. package/packages/solidjs/src/pages/SEOAdminPage.tsx +0 -104
  112. package/packages/solidjs/src/pages/SEOAnalyticsPage.tsx +0 -102
  113. package/packages/solidjs/src/pages/index.ts +0 -2
  114. package/packages/solidjs/src/primitives/index.ts +0 -4
  115. package/packages/solidjs/src/primitives/useSEO.ts +0 -27
  116. package/packages/solidjs/src/primitives/useSEOAdmin.ts +0 -42
  117. package/packages/solidjs/src/primitives/useSEOScore.ts +0 -7
  118. package/packages/solidjs/src/primitives/useSitemap.ts +0 -8
  119. package/packages/solidjs/tsconfig.json +0 -20
  120. package/packages/solidjs/tsup.config.ts +0 -12
  121. package/packages/solidjs-css/package.json +0 -35
  122. package/packages/solidjs-css/src/index.ts +0 -5
  123. package/packages/solidjs-css/src/primitives/index.ts +0 -1
  124. package/packages/solidjs-css/tsup.config.ts +0 -2
  125. package/pnpm-workspace.yaml +0 -2
  126. package/tsconfig.json +0 -23
@@ -0,0 +1,53 @@
1
+ export { OGMeta, PageMetaBuilder, RobotsTxt, SEOConfig, SEOConfigBuilder, SEOMeta, SeoProvider, SeoProviderProps, SitemapEntry, TwitterMeta, applyTitleTemplate, articleSchema, breadcrumbSchema, buildAlternates, buildCanonical, buildTitle, calcSEOScore, configureSEO, createPageMeta, createSEOConfig, estimateReadingTime, faqSchema, generateRobotsTxt, generateSitemapXml, orgSchema, productSchema, seoPresets, truncateDescription, useCanonical, useSEO, useSEOAdmin, useSEOContext, useSEOScore, useSitemap, useStructuredData, validateSEOMeta, webApplicationSchema } from '@geenius/seo-react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { SEOMeta, SitemapEntry } from '@geenius/seo-shared';
4
+
5
+ interface SEOHeadProps {
6
+ meta: SEOMeta;
7
+ }
8
+ declare function SEOHead({ meta }: SEOHeadProps): react_jsx_runtime.JSX.Element;
9
+
10
+ interface SEOPreviewProps {
11
+ meta: SEOMeta;
12
+ }
13
+ declare function SEOPreview({ meta }: SEOPreviewProps): react_jsx_runtime.JSX.Element;
14
+
15
+ interface SEOScoreCardProps {
16
+ meta: SEOMeta;
17
+ }
18
+ declare function SEOScoreCard({ meta }: SEOScoreCardProps): react_jsx_runtime.JSX.Element;
19
+
20
+ interface MetaEditorProps {
21
+ meta: SEOMeta;
22
+ onChange: (meta: SEOMeta) => void;
23
+ }
24
+ declare function MetaEditor({ meta, onChange }: MetaEditorProps): react_jsx_runtime.JSX.Element;
25
+
26
+ interface SitemapViewerProps {
27
+ entries: SitemapEntry[];
28
+ }
29
+ declare function SitemapViewer({ entries }: SitemapViewerProps): react_jsx_runtime.JSX.Element;
30
+
31
+ interface BreadcrumbsJsonLdProps {
32
+ items: {
33
+ name: string;
34
+ url: string;
35
+ }[];
36
+ }
37
+ declare function BreadcrumbsJsonLd({ items }: BreadcrumbsJsonLdProps): react_jsx_runtime.JSX.Element;
38
+
39
+ interface ArticleJsonLdProps {
40
+ title: string;
41
+ description: string;
42
+ author: string;
43
+ datePublished: string;
44
+ url: string;
45
+ image?: string;
46
+ }
47
+ declare function ArticleJsonLd({ title, description, author, datePublished, url, image, }: ArticleJsonLdProps): react_jsx_runtime.JSX.Element;
48
+
49
+ declare function SEOAdminPage(): react_jsx_runtime.JSX.Element;
50
+
51
+ declare function SEOAnalyticsPage(): react_jsx_runtime.JSX.Element;
52
+
53
+ export { ArticleJsonLd, BreadcrumbsJsonLd, MetaEditor, SEOAdminPage, SEOAnalyticsPage, SEOHead, SEOPreview, SEOScoreCard, SitemapViewer };
@@ -0,0 +1,53 @@
1
+ export { OGMeta, PageMetaBuilder, RobotsTxt, SEOConfig, SEOConfigBuilder, SEOMeta, SeoProvider, SeoProviderProps, SitemapEntry, TwitterMeta, applyTitleTemplate, articleSchema, breadcrumbSchema, buildAlternates, buildCanonical, buildTitle, calcSEOScore, configureSEO, createPageMeta, createSEOConfig, estimateReadingTime, faqSchema, generateRobotsTxt, generateSitemapXml, orgSchema, productSchema, seoPresets, truncateDescription, useCanonical, useSEO, useSEOAdmin, useSEOContext, useSEOScore, useSitemap, useStructuredData, validateSEOMeta, webApplicationSchema } from '@geenius/seo-react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { SEOMeta, SitemapEntry } from '@geenius/seo-shared';
4
+
5
+ interface SEOHeadProps {
6
+ meta: SEOMeta;
7
+ }
8
+ declare function SEOHead({ meta }: SEOHeadProps): react_jsx_runtime.JSX.Element;
9
+
10
+ interface SEOPreviewProps {
11
+ meta: SEOMeta;
12
+ }
13
+ declare function SEOPreview({ meta }: SEOPreviewProps): react_jsx_runtime.JSX.Element;
14
+
15
+ interface SEOScoreCardProps {
16
+ meta: SEOMeta;
17
+ }
18
+ declare function SEOScoreCard({ meta }: SEOScoreCardProps): react_jsx_runtime.JSX.Element;
19
+
20
+ interface MetaEditorProps {
21
+ meta: SEOMeta;
22
+ onChange: (meta: SEOMeta) => void;
23
+ }
24
+ declare function MetaEditor({ meta, onChange }: MetaEditorProps): react_jsx_runtime.JSX.Element;
25
+
26
+ interface SitemapViewerProps {
27
+ entries: SitemapEntry[];
28
+ }
29
+ declare function SitemapViewer({ entries }: SitemapViewerProps): react_jsx_runtime.JSX.Element;
30
+
31
+ interface BreadcrumbsJsonLdProps {
32
+ items: {
33
+ name: string;
34
+ url: string;
35
+ }[];
36
+ }
37
+ declare function BreadcrumbsJsonLd({ items }: BreadcrumbsJsonLdProps): react_jsx_runtime.JSX.Element;
38
+
39
+ interface ArticleJsonLdProps {
40
+ title: string;
41
+ description: string;
42
+ author: string;
43
+ datePublished: string;
44
+ url: string;
45
+ image?: string;
46
+ }
47
+ declare function ArticleJsonLd({ title, description, author, datePublished, url, image, }: ArticleJsonLdProps): react_jsx_runtime.JSX.Element;
48
+
49
+ declare function SEOAdminPage(): react_jsx_runtime.JSX.Element;
50
+
51
+ declare function SEOAnalyticsPage(): react_jsx_runtime.JSX.Element;
52
+
53
+ export { ArticleJsonLd, BreadcrumbsJsonLd, MetaEditor, SEOAdminPage, SEOAnalyticsPage, SEOHead, SEOPreview, SEOScoreCard, SitemapViewer };
@@ -0,0 +1,539 @@
1
+ // src/index.ts
2
+ import {
3
+ buildTitle,
4
+ applyTitleTemplate,
5
+ truncateDescription,
6
+ buildCanonical,
7
+ buildAlternates,
8
+ estimateReadingTime,
9
+ calcSEOScore,
10
+ generateRobotsTxt,
11
+ generateSitemapXml,
12
+ articleSchema as articleSchema2,
13
+ productSchema,
14
+ orgSchema,
15
+ breadcrumbSchema as breadcrumbSchema2,
16
+ faqSchema,
17
+ webApplicationSchema,
18
+ SEOConfigBuilder,
19
+ createSEOConfig,
20
+ configureSEO,
21
+ PageMetaBuilder,
22
+ createPageMeta,
23
+ validateSEOMeta,
24
+ seoPresets,
25
+ SeoProvider,
26
+ useSEOContext,
27
+ useSEO,
28
+ useSEOAdmin as useSEOAdmin2,
29
+ useSEOScore as useSEOScore2,
30
+ useSitemap,
31
+ useCanonical,
32
+ useStructuredData
33
+ } from "@geenius/seo-react";
34
+
35
+ // src/components/SEOHead.tsx
36
+ import { useEffect } from "react";
37
+ import { jsx } from "react/jsx-runtime";
38
+ function SEOHead({ meta }) {
39
+ useEffect(() => {
40
+ document.title = meta.title;
41
+ const setMetaTag = (name, content, isProperty = false) => {
42
+ let tag = document.querySelector(
43
+ `meta[${isProperty ? "property" : "name"}="${name}"]`
44
+ );
45
+ if (!tag) {
46
+ tag = document.createElement("meta");
47
+ isProperty ? tag.setAttribute("property", name) : tag.setAttribute("name", name);
48
+ document.head.appendChild(tag);
49
+ }
50
+ tag.content = content;
51
+ };
52
+ setMetaTag("description", meta.description);
53
+ if (meta.keywords.length > 0) {
54
+ setMetaTag("keywords", meta.keywords.join(", "));
55
+ }
56
+ if (meta.robots) {
57
+ setMetaTag("robots", meta.robots);
58
+ }
59
+ setMetaTag("og:title", meta.og.title, true);
60
+ setMetaTag("og:description", meta.og.description, true);
61
+ if (meta.og.image) {
62
+ setMetaTag("og:image", meta.og.image, true);
63
+ if (meta.og.imageAlt) {
64
+ setMetaTag("og:image:alt", meta.og.imageAlt, true);
65
+ }
66
+ }
67
+ setMetaTag("og:type", meta.og.type, true);
68
+ setMetaTag("og:url", meta.og.url, true);
69
+ if (meta.og.siteName) {
70
+ setMetaTag("og:site_name", meta.og.siteName, true);
71
+ }
72
+ setMetaTag("twitter:card", meta.twitter.card);
73
+ setMetaTag("twitter:title", meta.twitter.title);
74
+ setMetaTag("twitter:description", meta.twitter.description);
75
+ if (meta.twitter.image) {
76
+ setMetaTag("twitter:image", meta.twitter.image);
77
+ }
78
+ if (meta.twitter.creator) {
79
+ setMetaTag("twitter:creator", meta.twitter.creator);
80
+ }
81
+ if (meta.canonical) {
82
+ let canonicalLink = document.querySelector('link[rel="canonical"]');
83
+ if (!canonicalLink) {
84
+ canonicalLink = document.createElement("link");
85
+ canonicalLink.rel = "canonical";
86
+ document.head.appendChild(canonicalLink);
87
+ }
88
+ canonicalLink.href = meta.canonical;
89
+ }
90
+ if (meta.alternates) {
91
+ Object.entries(meta.alternates).forEach(([lang, url]) => {
92
+ let altLink = document.querySelector(
93
+ `link[rel="alternate"][hreflang="${lang}"]`
94
+ );
95
+ if (!altLink) {
96
+ altLink = document.createElement("link");
97
+ altLink.rel = "alternate";
98
+ altLink.setAttribute("hreflang", lang);
99
+ document.head.appendChild(altLink);
100
+ }
101
+ altLink.href = url;
102
+ });
103
+ }
104
+ if (meta.jsonLd && meta.jsonLd.length > 0) {
105
+ meta.jsonLd.forEach((schema) => {
106
+ let script = document.querySelector('script[type="application/ld+json"]');
107
+ if (!script) {
108
+ script = document.createElement("script");
109
+ script.type = "application/ld+json";
110
+ document.head.appendChild(script);
111
+ }
112
+ script.textContent = JSON.stringify(schema);
113
+ });
114
+ }
115
+ }, [meta]);
116
+ return /* @__PURE__ */ jsx("div", { className: "seo__head" });
117
+ }
118
+
119
+ // src/components/SEOPreview.tsx
120
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
121
+ function SEOPreview({ meta }) {
122
+ return /* @__PURE__ */ jsxs("div", { className: "seo__preview", children: [
123
+ /* @__PURE__ */ jsxs("div", { children: [
124
+ /* @__PURE__ */ jsx2("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "0.5rem" }, children: "Google Search Preview" }),
125
+ /* @__PURE__ */ jsxs("div", { className: "seo__preview-google", children: [
126
+ /* @__PURE__ */ jsx2("div", { className: "seo__preview-google-url", children: new URL(meta.og.url).hostname }),
127
+ /* @__PURE__ */ jsx2("div", { className: "seo__preview-google-title", children: meta.title }),
128
+ /* @__PURE__ */ jsx2("div", { className: "seo__preview-google-desc", children: meta.description })
129
+ ] })
130
+ ] }),
131
+ /* @__PURE__ */ jsxs("div", { children: [
132
+ /* @__PURE__ */ jsx2("h3", { style: { fontSize: "0.875rem", fontWeight: 600, marginBottom: "0.5rem" }, children: "Social Media Preview" }),
133
+ /* @__PURE__ */ jsxs("div", { className: "seo__preview-social", children: [
134
+ meta.og.image && /* @__PURE__ */ jsx2(
135
+ "img",
136
+ {
137
+ src: meta.og.image,
138
+ alt: meta.og.imageAlt || meta.og.title,
139
+ className: "seo__preview-social-image"
140
+ }
141
+ ),
142
+ /* @__PURE__ */ jsxs("div", { className: "seo__preview-social-info", children: [
143
+ /* @__PURE__ */ jsx2("div", { className: "seo__preview-social-title", children: meta.og.title }),
144
+ /* @__PURE__ */ jsx2("div", { className: "seo__preview-social-desc", children: meta.og.description }),
145
+ /* @__PURE__ */ jsx2("div", { className: "seo__preview-social-url", children: meta.og.url })
146
+ ] })
147
+ ] })
148
+ ] })
149
+ ] });
150
+ }
151
+
152
+ // src/components/SEOScoreCard.tsx
153
+ import { useSEOScore } from "@geenius/seo-react";
154
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
155
+ function SEOScoreCard({ meta }) {
156
+ const { score, issues } = useSEOScore(meta);
157
+ const scoreClass = score >= 80 ? "seo__score-number--good" : score >= 50 ? "seo__score-number--fair" : "seo__score-number--poor";
158
+ const barClass = score >= 80 ? "seo__score-bar-fill--good" : score >= 50 ? "seo__score-bar-fill--fair" : "seo__score-bar-fill--poor";
159
+ return /* @__PURE__ */ jsxs2("div", { className: "seo__score-card", children: [
160
+ /* @__PURE__ */ jsxs2("div", { className: "seo__score-card-header", children: [
161
+ /* @__PURE__ */ jsx3("div", { className: "seo__score-card-title", children: "SEO Score" }),
162
+ /* @__PURE__ */ jsxs2("span", { className: `seo__score-number ${scoreClass}`, children: [
163
+ score,
164
+ "/100"
165
+ ] })
166
+ ] }),
167
+ /* @__PURE__ */ jsx3("div", { className: "seo__score-bar", children: /* @__PURE__ */ jsx3("div", { className: `seo__score-bar-fill ${barClass}`, style: { width: `${score}%` } }) }),
168
+ issues.length > 0 && /* @__PURE__ */ jsxs2("div", { children: [
169
+ /* @__PURE__ */ jsx3("div", { className: "seo__issue-list-title", children: "Issues Found" }),
170
+ /* @__PURE__ */ jsx3("ul", { className: "seo__issue-list", children: issues.map((issue, i) => /* @__PURE__ */ jsxs2("li", { className: "seo__issue-item", children: [
171
+ /* @__PURE__ */ jsx3("span", { className: "seo__issue-item-bullet", children: "\u2022" }),
172
+ issue
173
+ ] }, i)) })
174
+ ] })
175
+ ] });
176
+ }
177
+
178
+ // src/components/MetaEditor.tsx
179
+ import { useState } from "react";
180
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
181
+ function MetaEditor({ meta, onChange }) {
182
+ const [localMeta, setLocalMeta] = useState(meta);
183
+ const handleChange = (updates) => {
184
+ const updated = { ...localMeta, ...updates };
185
+ setLocalMeta(updated);
186
+ onChange(updated);
187
+ };
188
+ return /* @__PURE__ */ jsxs3("div", { className: "seo__meta-editor", children: [
189
+ /* @__PURE__ */ jsxs3("div", { className: "seo__meta-field", children: [
190
+ /* @__PURE__ */ jsxs3("label", { className: "seo__meta-field-label", children: [
191
+ "Title (",
192
+ localMeta.title.length,
193
+ "/60)"
194
+ ] }),
195
+ /* @__PURE__ */ jsx4(
196
+ "input",
197
+ {
198
+ type: "text",
199
+ value: localMeta.title,
200
+ onChange: (e) => handleChange({
201
+ title: e.target.value.slice(0, 60)
202
+ }),
203
+ className: "seo__meta-field-input",
204
+ placeholder: "Page title"
205
+ }
206
+ ),
207
+ /* @__PURE__ */ jsx4("div", { className: "seo__meta-char-count", children: localMeta.title.length < 30 ? "Too short" : localMeta.title.length > 60 ? "Too long" : "Good length" })
208
+ ] }),
209
+ /* @__PURE__ */ jsxs3("div", { className: "seo__meta-field", children: [
210
+ /* @__PURE__ */ jsxs3("label", { className: "seo__meta-field-label", children: [
211
+ "Description (",
212
+ localMeta.description.length,
213
+ "/160)"
214
+ ] }),
215
+ /* @__PURE__ */ jsx4(
216
+ "textarea",
217
+ {
218
+ value: localMeta.description,
219
+ onChange: (e) => handleChange({
220
+ description: e.target.value.slice(0, 160)
221
+ }),
222
+ className: "seo__meta-field-textarea",
223
+ placeholder: "Page description"
224
+ }
225
+ ),
226
+ /* @__PURE__ */ jsx4("div", { className: "seo__meta-char-count", children: localMeta.description.length < 50 ? "Too short" : localMeta.description.length > 160 ? "Too long" : "Good length" })
227
+ ] }),
228
+ /* @__PURE__ */ jsxs3("div", { className: "seo__meta-field", children: [
229
+ /* @__PURE__ */ jsx4("label", { className: "seo__meta-field-label", children: "Keywords" }),
230
+ /* @__PURE__ */ jsx4(
231
+ "input",
232
+ {
233
+ type: "text",
234
+ value: localMeta.keywords.join(", "),
235
+ onChange: (e) => handleChange({
236
+ keywords: e.target.value.split(",").map((k) => k.trim())
237
+ }),
238
+ className: "seo__meta-field-input",
239
+ placeholder: "keyword1, keyword2, keyword3"
240
+ }
241
+ )
242
+ ] }),
243
+ /* @__PURE__ */ jsxs3("div", { className: "seo__meta-field", children: [
244
+ /* @__PURE__ */ jsx4("label", { className: "seo__meta-field-label", children: "Canonical URL" }),
245
+ /* @__PURE__ */ jsx4(
246
+ "input",
247
+ {
248
+ type: "url",
249
+ value: localMeta.canonical || "",
250
+ onChange: (e) => handleChange({
251
+ canonical: e.target.value
252
+ }),
253
+ className: "seo__meta-field-input",
254
+ placeholder: "https://example.com/page"
255
+ }
256
+ )
257
+ ] }),
258
+ /* @__PURE__ */ jsxs3("div", { className: "seo__meta-field", children: [
259
+ /* @__PURE__ */ jsx4("label", { className: "seo__meta-field-label", children: "OG Image URL" }),
260
+ /* @__PURE__ */ jsx4(
261
+ "input",
262
+ {
263
+ type: "url",
264
+ value: localMeta.og.image || "",
265
+ onChange: (e) => handleChange({
266
+ og: { ...localMeta.og, image: e.target.value }
267
+ }),
268
+ className: "seo__meta-field-input",
269
+ placeholder: "https://example.com/image.jpg"
270
+ }
271
+ )
272
+ ] }),
273
+ /* @__PURE__ */ jsxs3("div", { className: "seo__meta-field", children: [
274
+ /* @__PURE__ */ jsx4("label", { className: "seo__meta-field-label", children: "Twitter Card Type" }),
275
+ /* @__PURE__ */ jsxs3(
276
+ "select",
277
+ {
278
+ value: localMeta.twitter.card,
279
+ onChange: (e) => handleChange({
280
+ twitter: {
281
+ ...localMeta.twitter,
282
+ card: e.target.value
283
+ }
284
+ }),
285
+ className: "seo__meta-field-select",
286
+ children: [
287
+ /* @__PURE__ */ jsx4("option", { value: "summary", children: "Summary" }),
288
+ /* @__PURE__ */ jsx4("option", { value: "summary_large_image", children: "Summary Large Image" })
289
+ ]
290
+ }
291
+ )
292
+ ] }),
293
+ /* @__PURE__ */ jsxs3("div", { className: "seo__meta-field", children: [
294
+ /* @__PURE__ */ jsx4("label", { className: "seo__meta-field-label", children: "Robots Directive" }),
295
+ /* @__PURE__ */ jsx4(
296
+ "input",
297
+ {
298
+ type: "text",
299
+ value: localMeta.robots || "",
300
+ onChange: (e) => handleChange({
301
+ robots: e.target.value
302
+ }),
303
+ className: "seo__meta-field-input",
304
+ placeholder: "index, follow"
305
+ }
306
+ )
307
+ ] })
308
+ ] });
309
+ }
310
+
311
+ // src/components/SitemapViewer.tsx
312
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
313
+ function SitemapViewer({ entries }) {
314
+ return /* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsxs4("table", { className: "seo__sitemap-table", children: [
315
+ /* @__PURE__ */ jsx5("thead", { className: "seo__sitemap-table-head", children: /* @__PURE__ */ jsxs4("tr", { children: [
316
+ /* @__PURE__ */ jsx5("th", { className: "seo__sitemap-table-header", children: "URL" }),
317
+ /* @__PURE__ */ jsx5("th", { className: "seo__sitemap-table-header", children: "Last Modified" }),
318
+ /* @__PURE__ */ jsx5("th", { className: "seo__sitemap-table-header", children: "Change Frequency" }),
319
+ /* @__PURE__ */ jsx5("th", { className: "seo__sitemap-table-header", children: "Priority" })
320
+ ] }) }),
321
+ /* @__PURE__ */ jsx5("tbody", { children: entries.map((entry, i) => /* @__PURE__ */ jsxs4("tr", { className: "seo__sitemap-row", children: [
322
+ /* @__PURE__ */ jsx5("td", { className: "seo__sitemap-cell", children: /* @__PURE__ */ jsx5("a", { href: entry.url, target: "_blank", rel: "noopener noreferrer", className: "seo__sitemap-url", children: entry.url }) }),
323
+ /* @__PURE__ */ jsx5("td", { className: "seo__sitemap-cell seo__sitemap-cell-muted", children: entry.lastmod || "-" }),
324
+ /* @__PURE__ */ jsx5("td", { className: "seo__sitemap-cell seo__sitemap-cell-muted", children: entry.changefreq || "-" }),
325
+ /* @__PURE__ */ jsx5("td", { className: "seo__sitemap-cell seo__sitemap-cell-muted", children: entry.priority || "-" })
326
+ ] }, i)) })
327
+ ] }) });
328
+ }
329
+
330
+ // src/components/BreadcrumbsJsonLd.tsx
331
+ import { useEffect as useEffect2 } from "react";
332
+ import { breadcrumbSchema } from "@geenius/seo-shared";
333
+ import { jsx as jsx6 } from "react/jsx-runtime";
334
+ function BreadcrumbsJsonLd({ items }) {
335
+ useEffect2(() => {
336
+ const script = document.createElement("script");
337
+ script.type = "application/ld+json";
338
+ script.textContent = JSON.stringify(breadcrumbSchema(items));
339
+ document.head.appendChild(script);
340
+ return () => {
341
+ document.head.removeChild(script);
342
+ };
343
+ }, [items]);
344
+ return /* @__PURE__ */ jsx6("div", { className: "seo__breadcrumbs" });
345
+ }
346
+
347
+ // src/components/ArticleJsonLd.tsx
348
+ import { useEffect as useEffect3 } from "react";
349
+ import { articleSchema } from "@geenius/seo-shared";
350
+ import { jsx as jsx7 } from "react/jsx-runtime";
351
+ function ArticleJsonLd({
352
+ title,
353
+ description,
354
+ author,
355
+ datePublished,
356
+ url,
357
+ image
358
+ }) {
359
+ useEffect3(() => {
360
+ const script = document.createElement("script");
361
+ script.type = "application/ld+json";
362
+ script.textContent = JSON.stringify(
363
+ articleSchema({
364
+ title,
365
+ description,
366
+ author,
367
+ datePublished,
368
+ url,
369
+ image
370
+ })
371
+ );
372
+ document.head.appendChild(script);
373
+ return () => {
374
+ document.head.removeChild(script);
375
+ };
376
+ }, [title, description, author, datePublished, url, image]);
377
+ return /* @__PURE__ */ jsx7("div", { className: "seo__article-jsonld" });
378
+ }
379
+
380
+ // src/pages/SEOAdminPage.tsx
381
+ import { useState as useState2 } from "react";
382
+ import { useSEOAdmin } from "@geenius/seo-react";
383
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
384
+ function SEOAdminPage() {
385
+ const { pages, upsertMeta, deleteMeta, isLoading } = useSEOAdmin();
386
+ const [selectedPath, setSelectedPath] = useState2(null);
387
+ const [editingMeta, setEditingMeta] = useState2(null);
388
+ const currentPage = pages.find((p) => p.path === selectedPath);
389
+ const handleSave = async () => {
390
+ if (selectedPath && editingMeta) {
391
+ await upsertMeta(selectedPath, editingMeta);
392
+ setEditingMeta(null);
393
+ }
394
+ };
395
+ const handleDelete = async () => {
396
+ if (selectedPath) {
397
+ await deleteMeta(selectedPath);
398
+ setSelectedPath(null);
399
+ setEditingMeta(null);
400
+ }
401
+ };
402
+ if (isLoading) return /* @__PURE__ */ jsx8("div", { style: { padding: "1rem" }, children: "Loading..." });
403
+ return /* @__PURE__ */ jsxs5("div", { className: "seo__admin-container", children: [
404
+ /* @__PURE__ */ jsx8("h1", { className: "seo__admin-title", children: "SEO Admin" }),
405
+ /* @__PURE__ */ jsxs5("div", { className: "seo__admin-grid", children: [
406
+ /* @__PURE__ */ jsxs5("div", { className: "seo__admin-sidebar", children: [
407
+ /* @__PURE__ */ jsx8("div", { className: "seo__admin-sidebar-title", children: "Pages" }),
408
+ /* @__PURE__ */ jsx8("div", { className: "seo__admin-page-list", children: pages.map((page) => /* @__PURE__ */ jsx8(
409
+ "button",
410
+ {
411
+ onClick: () => {
412
+ setSelectedPath(page.path);
413
+ setEditingMeta(page.meta);
414
+ },
415
+ className: `seo__admin-page-button ${selectedPath === page.path ? "seo__admin-page-button--active" : ""}`,
416
+ children: page.path || "/"
417
+ },
418
+ page.path
419
+ )) })
420
+ ] }),
421
+ currentPage && editingMeta && /* @__PURE__ */ jsxs5("div", { className: "seo__admin-editor", children: [
422
+ /* @__PURE__ */ jsxs5("div", { children: [
423
+ /* @__PURE__ */ jsx8("h2", { style: { fontWeight: 600, marginBottom: "1rem" }, children: "Edit Metadata" }),
424
+ /* @__PURE__ */ jsx8(MetaEditor, { meta: editingMeta, onChange: setEditingMeta })
425
+ ] }),
426
+ /* @__PURE__ */ jsxs5("div", { className: "seo__space-y", children: [
427
+ /* @__PURE__ */ jsxs5("div", { children: [
428
+ /* @__PURE__ */ jsx8("h2", { style: { fontWeight: 600, marginBottom: "1rem" }, children: "Preview" }),
429
+ /* @__PURE__ */ jsx8(SEOPreview, { meta: editingMeta })
430
+ ] }),
431
+ /* @__PURE__ */ jsx8(SEOScoreCard, { meta: editingMeta })
432
+ ] })
433
+ ] })
434
+ ] }),
435
+ currentPage && editingMeta && /* @__PURE__ */ jsxs5("div", { className: "seo__admin-actions", children: [
436
+ /* @__PURE__ */ jsx8("button", { onClick: handleSave, className: "seo__button seo__button--success", children: "Save" }),
437
+ /* @__PURE__ */ jsx8("button", { onClick: handleDelete, className: "seo__button seo__button--danger", children: "Delete" })
438
+ ] })
439
+ ] });
440
+ }
441
+
442
+ // src/pages/SEOAnalyticsPage.tsx
443
+ import { useState as useState3, useEffect as useEffect4 } from "react";
444
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
445
+ function SEOAnalyticsPage() {
446
+ const [topPages, setTopPages] = useState3([]);
447
+ const [isLoading, setIsLoading] = useState3(false);
448
+ useEffect4(() => {
449
+ setIsLoading(true);
450
+ setIsLoading(false);
451
+ }, []);
452
+ const totalViews = topPages.reduce((sum, p) => sum + p.views, 0);
453
+ const avgTimeOnPage = topPages.length > 0 ? topPages.reduce((sum, p) => sum + p.avgTimeOnPage, 0) / topPages.length : 0;
454
+ const avgBounceRate = topPages.length > 0 ? topPages.reduce((sum, p) => sum + p.bounceRate, 0) / topPages.length * 100 : 0;
455
+ if (isLoading) return /* @__PURE__ */ jsx9("div", { style: { padding: "1rem" }, children: "Loading..." });
456
+ return /* @__PURE__ */ jsxs6("div", { className: "seo__analytics-container", children: [
457
+ /* @__PURE__ */ jsx9("h1", { className: "seo__analytics-title", children: "SEO Analytics" }),
458
+ /* @__PURE__ */ jsx9("div", { className: "seo__analytics-table-wrapper", children: /* @__PURE__ */ jsxs6("table", { className: "seo__sitemap-table", children: [
459
+ /* @__PURE__ */ jsx9("thead", { className: "seo__sitemap-table-head", children: /* @__PURE__ */ jsxs6("tr", { children: [
460
+ /* @__PURE__ */ jsx9("th", { className: "seo__sitemap-table-header", children: "Page" }),
461
+ /* @__PURE__ */ jsx9("th", { className: "seo__sitemap-table-header", children: "Views" }),
462
+ /* @__PURE__ */ jsx9("th", { className: "seo__sitemap-table-header", children: "Avg Time (s)" }),
463
+ /* @__PURE__ */ jsx9("th", { className: "seo__sitemap-table-header", children: "Bounce Rate" })
464
+ ] }) }),
465
+ /* @__PURE__ */ jsx9("tbody", { children: topPages.map((page, i) => /* @__PURE__ */ jsxs6("tr", { className: "seo__sitemap-row", children: [
466
+ /* @__PURE__ */ jsx9("td", { className: "seo__sitemap-cell", children: page.path }),
467
+ /* @__PURE__ */ jsx9("td", { className: "seo__sitemap-cell", children: page.views.toLocaleString() }),
468
+ /* @__PURE__ */ jsx9("td", { className: "seo__sitemap-cell", children: page.avgTimeOnPage.toFixed(1) }),
469
+ /* @__PURE__ */ jsxs6("td", { className: "seo__sitemap-cell", children: [
470
+ (page.bounceRate * 100).toFixed(1),
471
+ "%"
472
+ ] })
473
+ ] }, i)) })
474
+ ] }) }),
475
+ topPages.length === 0 && /* @__PURE__ */ jsx9("p", { style: { textAlign: "center", color: "var(--seo-text-muted)", padding: "2rem" }, children: "No analytics data available" }),
476
+ /* @__PURE__ */ jsxs6("div", { className: "seo__analytics-summary", children: [
477
+ /* @__PURE__ */ jsxs6("div", { className: "seo__analytics-card", children: [
478
+ /* @__PURE__ */ jsx9("div", { className: "seo__analytics-card-label", children: "Total Views" }),
479
+ /* @__PURE__ */ jsx9("div", { className: "seo__analytics-card-value", children: totalViews.toLocaleString() })
480
+ ] }),
481
+ /* @__PURE__ */ jsxs6("div", { className: "seo__analytics-card", children: [
482
+ /* @__PURE__ */ jsx9("div", { className: "seo__analytics-card-label", children: "Average Time on Page" }),
483
+ /* @__PURE__ */ jsxs6("div", { className: "seo__analytics-card-value", children: [
484
+ avgTimeOnPage.toFixed(1),
485
+ "s"
486
+ ] })
487
+ ] }),
488
+ /* @__PURE__ */ jsxs6("div", { className: "seo__analytics-card", children: [
489
+ /* @__PURE__ */ jsx9("div", { className: "seo__analytics-card-label", children: "Average Bounce Rate" }),
490
+ /* @__PURE__ */ jsxs6("div", { className: "seo__analytics-card-value", children: [
491
+ avgBounceRate.toFixed(1),
492
+ "%"
493
+ ] })
494
+ ] })
495
+ ] })
496
+ ] });
497
+ }
498
+ export {
499
+ ArticleJsonLd,
500
+ BreadcrumbsJsonLd,
501
+ MetaEditor,
502
+ PageMetaBuilder,
503
+ SEOAdminPage,
504
+ SEOAnalyticsPage,
505
+ SEOConfigBuilder,
506
+ SEOHead,
507
+ SEOPreview,
508
+ SEOScoreCard,
509
+ SeoProvider,
510
+ SitemapViewer,
511
+ applyTitleTemplate,
512
+ articleSchema2 as articleSchema,
513
+ breadcrumbSchema2 as breadcrumbSchema,
514
+ buildAlternates,
515
+ buildCanonical,
516
+ buildTitle,
517
+ calcSEOScore,
518
+ configureSEO,
519
+ createPageMeta,
520
+ createSEOConfig,
521
+ estimateReadingTime,
522
+ faqSchema,
523
+ generateRobotsTxt,
524
+ generateSitemapXml,
525
+ orgSchema,
526
+ productSchema,
527
+ seoPresets,
528
+ truncateDescription,
529
+ useCanonical,
530
+ useSEO,
531
+ useSEOAdmin2 as useSEOAdmin,
532
+ useSEOContext,
533
+ useSEOScore2 as useSEOScore,
534
+ useSitemap,
535
+ useStructuredData,
536
+ validateSEOMeta,
537
+ webApplicationSchema
538
+ };
539
+ //# sourceMappingURL=index.js.map