@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
@@ -1,96 +0,0 @@
1
- import { useState, useEffect } from 'react'
2
-
3
- interface AnalyticsSummary {
4
- path: string
5
- views: number
6
- avgTimeOnPage: number
7
- bounceRate: number
8
- }
9
-
10
- export function SEOAnalyticsPage() {
11
- const [topPages, setTopPages] = useState<AnalyticsSummary[]>([])
12
- const [isLoading, setIsLoading] = useState(false)
13
-
14
- useEffect(() => {
15
- setIsLoading(true)
16
- // Placeholder for Convex query integration
17
- // const result = await client.query(api.queries.getTopPages, { limit: 10 })
18
- // setTopPages(result)
19
- setIsLoading(false)
20
- }, [])
21
-
22
- if (isLoading) return <div className="p-4">Loading...</div>
23
-
24
- return (
25
- <div className="max-w-6xl mx-auto p-6">
26
- <h1 className="text-3xl font-bold mb-6">SEO Analytics</h1>
27
-
28
- <div className="space-y-6">
29
- {/* Top Pages */}
30
- <div className="border border-gray-200 rounded p-4">
31
- <h2 className="font-semibold mb-4">Top Pages by Views</h2>
32
-
33
- <div className="overflow-x-auto">
34
- <table className="w-full text-sm">
35
- <thead className="bg-gray-100 border-b">
36
- <tr>
37
- <th className="text-left p-3">Page</th>
38
- <th className="text-right p-3">Views</th>
39
- <th className="text-right p-3">Avg Time (s)</th>
40
- <th className="text-right p-3">Bounce Rate</th>
41
- </tr>
42
- </thead>
43
- <tbody>
44
- {topPages.map((page, i) => (
45
- <tr key={i} className="border-b hover:bg-gray-50">
46
- <td className="p-3 font-mono text-xs">{page.path}</td>
47
- <td className="p-3 text-right">{page.views.toLocaleString()}</td>
48
- <td className="p-3 text-right">{page.avgTimeOnPage.toFixed(1)}</td>
49
- <td className="p-3 text-right">{(page.bounceRate * 100).toFixed(1)}%</td>
50
- </tr>
51
- ))}
52
- </tbody>
53
- </table>
54
- </div>
55
-
56
- {topPages.length === 0 && (
57
- <p className="text-center text-gray-500 py-8">No analytics data available</p>
58
- )}
59
- </div>
60
-
61
- {/* Analytics Summary */}
62
- <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
63
- <div className="border border-gray-200 rounded p-4">
64
- <h3 className="text-sm text-gray-600 mb-1">Total Views</h3>
65
- <p className="text-2xl font-bold">
66
- {topPages.reduce((sum, p) => sum + p.views, 0).toLocaleString()}
67
- </p>
68
- </div>
69
-
70
- <div className="border border-gray-200 rounded p-4">
71
- <h3 className="text-sm text-gray-600 mb-1">Average Time on Page</h3>
72
- <p className="text-2xl font-bold">
73
- {topPages.length > 0
74
- ? (topPages.reduce((sum, p) => sum + p.avgTimeOnPage, 0) / topPages.length).toFixed(1)
75
- : '0'}
76
- s
77
- </p>
78
- </div>
79
-
80
- <div className="border border-gray-200 rounded p-4">
81
- <h3 className="text-sm text-gray-600 mb-1">Average Bounce Rate</h3>
82
- <p className="text-2xl font-bold">
83
- {topPages.length > 0
84
- ? (
85
- (topPages.reduce((sum, p) => sum + p.bounceRate, 0) / topPages.length) *
86
- 100
87
- ).toFixed(1)
88
- : '0'}
89
- %
90
- </p>
91
- </div>
92
- </div>
93
- </div>
94
- </div>
95
- )
96
- }
@@ -1,2 +0,0 @@
1
- export * from './SEOAdminPage'
2
- export * from './SEOAnalyticsPage'
@@ -1,19 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src",
6
- "jsx": "react-jsx",
7
- "strict": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "resolveJsonModule": true,
11
- "isolatedModules": true,
12
- "target": "ES2022",
13
- "module": "ESNext",
14
- "moduleResolution": "bundler"
15
- },
16
- "include": [
17
- "src"
18
- ]
19
- }
@@ -1,12 +0,0 @@
1
- import { defineConfig } from 'tsup'
2
-
3
- export default defineConfig({
4
- entry: { index: 'src/index.ts' },
5
- outDir: 'dist',
6
- format: ['esm'],
7
- dts: true,
8
- sourcemap: true,
9
- clean: true,
10
- treeshake: true,
11
- external: ['react', 'react-dom'],
12
- })
@@ -1,36 +0,0 @@
1
- {
2
- "name": "@geenius-seo/react-css",
3
- "version": "0.1.0",
4
- "description": "Geenius SEO — react components with vanilla CSS",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.mjs",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "import": "./dist/index.mjs",
12
- "require": "./dist/index.js",
13
- "types": "./dist/index.d.ts"
14
- }
15
- },
16
- "scripts": {
17
- "build": "tsup",
18
- "lint": "tsc --noEmit",
19
- "clean": "rm -rf dist"
20
- },
21
- "files": ["dist"],
22
- "publishConfig": { "access": "public" },
23
- "peerDependencies": { "react": ">=18.0.0" },
24
- "dependencies": {
25
- "@geenius-seo/shared": "workspace:*"
26
- },
27
- "devDependencies": {
28
- "@types/react": "^19.0.0",
29
- "react": "^19.2.4",
30
- "tsup": "^8.5.1",
31
- "typescript": "~6.0.2"
32
- },
33
- "author": "Antigravity HQ",
34
- "license": "MIT",
35
- "engines": { "node": ">=20.0.0" }
36
- }
@@ -1,42 +0,0 @@
1
- import { useEffect } from 'react'
2
- import { articleSchema } from '@geenius-seo/shared'
3
-
4
- interface ArticleJsonLdProps {
5
- title: string
6
- description: string
7
- author: string
8
- datePublished: string
9
- url: string
10
- image?: string
11
- }
12
-
13
- export function ArticleJsonLd({
14
- title,
15
- description,
16
- author,
17
- datePublished,
18
- url,
19
- image,
20
- }: ArticleJsonLdProps) {
21
- useEffect(() => {
22
- const script = document.createElement('script')
23
- script.type = 'application/ld+json'
24
- script.textContent = JSON.stringify(
25
- articleSchema({
26
- title,
27
- description,
28
- author,
29
- datePublished,
30
- url,
31
- image,
32
- }),
33
- )
34
- document.head.appendChild(script)
35
-
36
- return () => {
37
- document.head.removeChild(script)
38
- }
39
- }, [title, description, author, datePublished, url, image])
40
-
41
- return <div className="seo__article-jsonld" />
42
- }
@@ -1,24 +0,0 @@
1
- import { useEffect } from 'react'
2
- import { breadcrumbSchema } from '@geenius-seo/shared'
3
-
4
- interface BreadcrumbsJsonLdProps {
5
- items: {
6
- name: string
7
- url: string
8
- }[]
9
- }
10
-
11
- export function BreadcrumbsJsonLd({ items }: BreadcrumbsJsonLdProps) {
12
- useEffect(() => {
13
- const script = document.createElement('script')
14
- script.type = 'application/ld+json'
15
- script.textContent = JSON.stringify(breadcrumbSchema(items))
16
- document.head.appendChild(script)
17
-
18
- return () => {
19
- document.head.removeChild(script)
20
- }
21
- }, [items])
22
-
23
- return <div className="seo__breadcrumbs" />
24
- }
@@ -1,147 +0,0 @@
1
- import { useState } from 'react'
2
- import type { SEOMeta } from '@geenius-seo/shared'
3
-
4
- interface MetaEditorProps {
5
- meta: SEOMeta
6
- onChange: (meta: SEOMeta) => void
7
- }
8
-
9
- export function MetaEditor({ meta, onChange }: MetaEditorProps) {
10
- const [localMeta, setLocalMeta] = useState(meta)
11
-
12
- const handleChange = (updates: Partial<SEOMeta>) => {
13
- const updated = { ...localMeta, ...updates }
14
- setLocalMeta(updated)
15
- onChange(updated)
16
- }
17
-
18
- return (
19
- <div className="seo__meta-editor">
20
- <div className="seo__meta-field">
21
- <label className="seo__meta-field-label">
22
- Title ({localMeta.title.length}/60)
23
- </label>
24
- <input
25
- type="text"
26
- value={localMeta.title}
27
- onChange={(e) =>
28
- handleChange({
29
- title: e.target.value.slice(0, 60),
30
- })
31
- }
32
- className="seo__meta-field-input"
33
- placeholder="Page title"
34
- />
35
- <div className="seo__meta-char-count">
36
- {localMeta.title.length < 30
37
- ? 'Too short'
38
- : localMeta.title.length > 60
39
- ? 'Too long'
40
- : 'Good length'}
41
- </div>
42
- </div>
43
-
44
- <div className="seo__meta-field">
45
- <label className="seo__meta-field-label">
46
- Description ({localMeta.description.length}/160)
47
- </label>
48
- <textarea
49
- value={localMeta.description}
50
- onChange={(e) =>
51
- handleChange({
52
- description: e.target.value.slice(0, 160),
53
- })
54
- }
55
- className="seo__meta-field-textarea"
56
- placeholder="Page description"
57
- />
58
- <div className="seo__meta-char-count">
59
- {localMeta.description.length < 50
60
- ? 'Too short'
61
- : localMeta.description.length > 160
62
- ? 'Too long'
63
- : 'Good length'}
64
- </div>
65
- </div>
66
-
67
- <div className="seo__meta-field">
68
- <label className="seo__meta-field-label">Keywords</label>
69
- <input
70
- type="text"
71
- value={localMeta.keywords.join(', ')}
72
- onChange={(e) =>
73
- handleChange({
74
- keywords: e.target.value.split(',').map((k) => k.trim()),
75
- })
76
- }
77
- className="seo__meta-field-input"
78
- placeholder="keyword1, keyword2, keyword3"
79
- />
80
- </div>
81
-
82
- <div className="seo__meta-field">
83
- <label className="seo__meta-field-label">Canonical URL</label>
84
- <input
85
- type="url"
86
- value={localMeta.canonical || ''}
87
- onChange={(e) =>
88
- handleChange({
89
- canonical: e.target.value,
90
- })
91
- }
92
- className="seo__meta-field-input"
93
- placeholder="https://example.com/page"
94
- />
95
- </div>
96
-
97
- <div className="seo__meta-field">
98
- <label className="seo__meta-field-label">OG Image URL</label>
99
- <input
100
- type="url"
101
- value={localMeta.og.image || ''}
102
- onChange={(e) =>
103
- handleChange({
104
- og: { ...localMeta.og, image: e.target.value },
105
- })
106
- }
107
- className="seo__meta-field-input"
108
- placeholder="https://example.com/image.jpg"
109
- />
110
- </div>
111
-
112
- <div className="seo__meta-field">
113
- <label className="seo__meta-field-label">Twitter Card Type</label>
114
- <select
115
- value={localMeta.twitter.card}
116
- onChange={(e) =>
117
- handleChange({
118
- twitter: {
119
- ...localMeta.twitter,
120
- card: e.target.value as 'summary' | 'summary_large_image',
121
- },
122
- })
123
- }
124
- className="seo__meta-field-select"
125
- >
126
- <option value="summary">Summary</option>
127
- <option value="summary_large_image">Summary Large Image</option>
128
- </select>
129
- </div>
130
-
131
- <div className="seo__meta-field">
132
- <label className="seo__meta-field-label">Robots Directive</label>
133
- <input
134
- type="text"
135
- value={localMeta.robots || ''}
136
- onChange={(e) =>
137
- handleChange({
138
- robots: e.target.value,
139
- })
140
- }
141
- className="seo__meta-field-input"
142
- placeholder="index, follow"
143
- />
144
- </div>
145
- </div>
146
- )
147
- }
@@ -1,95 +0,0 @@
1
- import { useEffect } from 'react'
2
- import type { SEOMeta } from '@geenius-seo/shared'
3
-
4
- interface SEOHeadProps {
5
- meta: SEOMeta
6
- }
7
-
8
- export function SEOHead({ meta }: SEOHeadProps) {
9
- useEffect(() => {
10
- document.title = meta.title
11
-
12
- const setMetaTag = (name: string, content: string, isProperty = false) => {
13
- let tag = document.querySelector(
14
- `meta[${isProperty ? 'property' : 'name'}="${name}"]`,
15
- ) as HTMLMetaElement
16
- if (!tag) {
17
- tag = document.createElement('meta')
18
- isProperty ? tag.setAttribute('property', name) : tag.setAttribute('name', name)
19
- document.head.appendChild(tag)
20
- }
21
- tag.content = content
22
- }
23
-
24
- setMetaTag('description', meta.description)
25
- if (meta.keywords.length > 0) {
26
- setMetaTag('keywords', meta.keywords.join(', '))
27
- }
28
- if (meta.robots) {
29
- setMetaTag('robots', meta.robots)
30
- }
31
-
32
- setMetaTag('og:title', meta.og.title, true)
33
- setMetaTag('og:description', meta.og.description, true)
34
- if (meta.og.image) {
35
- setMetaTag('og:image', meta.og.image, true)
36
- if (meta.og.imageAlt) {
37
- setMetaTag('og:image:alt', meta.og.imageAlt, true)
38
- }
39
- }
40
- setMetaTag('og:type', meta.og.type, true)
41
- setMetaTag('og:url', meta.og.url, true)
42
- if (meta.og.siteName) {
43
- setMetaTag('og:site_name', meta.og.siteName, true)
44
- }
45
-
46
- setMetaTag('twitter:card', meta.twitter.card)
47
- setMetaTag('twitter:title', meta.twitter.title)
48
- setMetaTag('twitter:description', meta.twitter.description)
49
- if (meta.twitter.image) {
50
- setMetaTag('twitter:image', meta.twitter.image)
51
- }
52
- if (meta.twitter.creator) {
53
- setMetaTag('twitter:creator', meta.twitter.creator)
54
- }
55
-
56
- if (meta.canonical) {
57
- let canonicalLink = document.querySelector('link[rel="canonical"]') as HTMLLinkElement
58
- if (!canonicalLink) {
59
- canonicalLink = document.createElement('link')
60
- canonicalLink.rel = 'canonical'
61
- document.head.appendChild(canonicalLink)
62
- }
63
- canonicalLink.href = meta.canonical
64
- }
65
-
66
- if (meta.alternates) {
67
- Object.entries(meta.alternates).forEach(([lang, url]) => {
68
- let altLink = document.querySelector(
69
- `link[rel="alternate"][hreflang="${lang}"]`,
70
- ) as HTMLLinkElement
71
- if (!altLink) {
72
- altLink = document.createElement('link')
73
- altLink.rel = 'alternate'
74
- altLink.hrefLang = lang
75
- document.head.appendChild(altLink)
76
- }
77
- altLink.href = url
78
- })
79
- }
80
-
81
- if (meta.jsonLd && meta.jsonLd.length > 0) {
82
- meta.jsonLd.forEach((schema) => {
83
- let script = document.querySelector('script[type="application/ld+json"]') as HTMLScriptElement
84
- if (!script) {
85
- script = document.createElement('script')
86
- script.type = 'application/ld+json'
87
- document.head.appendChild(script)
88
- }
89
- script.textContent = JSON.stringify(schema)
90
- })
91
- }
92
- }, [meta])
93
-
94
- return <div className="seo__head" />
95
- }
@@ -1,42 +0,0 @@
1
- import type { SEOMeta } from '@geenius-seo/shared'
2
-
3
- interface SEOPreviewProps {
4
- meta: SEOMeta
5
- }
6
-
7
- export function SEOPreview({ meta }: SEOPreviewProps) {
8
- return (
9
- <div className="seo__preview">
10
- <div>
11
- <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }}>
12
- Google Search Preview
13
- </h3>
14
- <div className="seo__preview-google">
15
- <div className="seo__preview-google-url">{new URL(meta.og.url).hostname}</div>
16
- <div className="seo__preview-google-title">{meta.title}</div>
17
- <div className="seo__preview-google-desc">{meta.description}</div>
18
- </div>
19
- </div>
20
-
21
- <div>
22
- <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }}>
23
- Social Media Preview
24
- </h3>
25
- <div className="seo__preview-social">
26
- {meta.og.image && (
27
- <img
28
- src={meta.og.image}
29
- alt={meta.og.imageAlt || meta.og.title}
30
- className="seo__preview-social-image"
31
- />
32
- )}
33
- <div className="seo__preview-social-info">
34
- <div className="seo__preview-social-title">{meta.og.title}</div>
35
- <div className="seo__preview-social-desc">{meta.og.description}</div>
36
- <div className="seo__preview-social-url">{meta.og.url}</div>
37
- </div>
38
- </div>
39
- </div>
40
- </div>
41
- )
42
- }
@@ -1,42 +0,0 @@
1
- import { useSEOScore } from '@geenius-seo/react'
2
- import type { SEOMeta } from '@geenius-seo/shared'
3
-
4
- interface SEOScoreCardProps {
5
- meta: SEOMeta
6
- }
7
-
8
- export function SEOScoreCard({ meta }: SEOScoreCardProps) {
9
- const { score, issues } = useSEOScore(meta)
10
-
11
- const scoreClass =
12
- score >= 80 ? 'seo__score-number--good' : score >= 50 ? 'seo__score-number--fair' : 'seo__score-number--poor'
13
- const barClass =
14
- score >= 80 ? 'seo__score-bar-fill--good' : score >= 50 ? 'seo__score-bar-fill--fair' : 'seo__score-bar-fill--poor'
15
-
16
- return (
17
- <div className="seo__score-card">
18
- <div className="seo__score-card-header">
19
- <div className="seo__score-card-title">SEO Score</div>
20
- <span className={`seo__score-number ${scoreClass}`}>{score}/100</span>
21
- </div>
22
-
23
- <div className="seo__score-bar">
24
- <div className={`seo__score-bar-fill ${barClass}`} style={{ width: `${score}%` }} />
25
- </div>
26
-
27
- {issues.length > 0 && (
28
- <div>
29
- <div className="seo__issue-list-title">Issues Found</div>
30
- <ul className="seo__issue-list">
31
- {issues.map((issue, i) => (
32
- <li key={i} className="seo__issue-item">
33
- <span className="seo__issue-item-bullet">•</span>
34
- {issue}
35
- </li>
36
- ))}
37
- </ul>
38
- </div>
39
- )}
40
- </div>
41
- )
42
- }
@@ -1,36 +0,0 @@
1
- import type { SitemapEntry } from '@geenius-seo/shared'
2
-
3
- interface SitemapViewerProps {
4
- entries: SitemapEntry[]
5
- }
6
-
7
- export function SitemapViewer({ entries }: SitemapViewerProps) {
8
- return (
9
- <div>
10
- <table className="seo__sitemap-table">
11
- <thead className="seo__sitemap-table-head">
12
- <tr>
13
- <th className="seo__sitemap-table-header">URL</th>
14
- <th className="seo__sitemap-table-header">Last Modified</th>
15
- <th className="seo__sitemap-table-header">Change Frequency</th>
16
- <th className="seo__sitemap-table-header">Priority</th>
17
- </tr>
18
- </thead>
19
- <tbody>
20
- {entries.map((entry, i) => (
21
- <tr key={i} className="seo__sitemap-row">
22
- <td className="seo__sitemap-cell">
23
- <a href={entry.url} target="_blank" rel="noopener noreferrer" className="seo__sitemap-url">
24
- {entry.url}
25
- </a>
26
- </td>
27
- <td className="seo__sitemap-cell seo__sitemap-cell-muted">{entry.lastmod || '-'}</td>
28
- <td className="seo__sitemap-cell seo__sitemap-cell-muted">{entry.changefreq || '-'}</td>
29
- <td className="seo__sitemap-cell seo__sitemap-cell-muted">{entry.priority || '-'}</td>
30
- </tr>
31
- ))}
32
- </tbody>
33
- </table>
34
- </div>
35
- )
36
- }
@@ -1,7 +0,0 @@
1
- export * from './SEOHead'
2
- export * from './SEOPreview'
3
- export * from './SEOScoreCard'
4
- export * from './MetaEditor'
5
- export * from './SitemapViewer'
6
- export * from './BreadcrumbsJsonLd'
7
- export * from './ArticleJsonLd'
@@ -1,9 +0,0 @@
1
- // Re-export all React hooks and types
2
- export * from '@geenius-seo/react'
3
-
4
- // CSS Components
5
- export * from './components'
6
- export * from './pages'
7
-
8
- // CSS Styles
9
- import './seo.css'