@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.
- package/package.json +16 -3
- package/packages/convex/dist/index.d.ts +56 -0
- package/packages/convex/dist/index.js +133 -0
- package/packages/convex/dist/index.js.map +1 -0
- package/packages/react/README.md +1 -1
- package/packages/react/dist/index.d.ts +156 -0
- package/packages/react/dist/index.js +567 -0
- package/packages/react/dist/index.js.map +1 -0
- package/packages/react-css/README.md +1 -1
- package/packages/react-css/dist/index.cjs +571 -0
- package/packages/react-css/dist/index.cjs.map +1 -0
- package/packages/react-css/{src/seo.css → dist/index.css} +7 -153
- package/packages/react-css/dist/index.css.map +1 -0
- package/packages/react-css/dist/index.d.cts +53 -0
- package/packages/react-css/dist/index.d.ts +53 -0
- package/packages/react-css/dist/index.js +539 -0
- package/packages/react-css/dist/index.js.map +1 -0
- package/packages/shared/README.md +1 -1
- package/packages/shared/dist/index.d.ts +262 -0
- package/packages/shared/dist/index.js +381 -0
- package/packages/shared/dist/index.js.map +1 -0
- package/packages/solidjs/README.md +1 -1
- package/packages/solidjs/dist/index.d.ts +133 -0
- package/packages/solidjs/dist/index.js +416 -0
- package/packages/solidjs/dist/index.js.map +1 -0
- package/packages/solidjs-css/README.md +1 -1
- package/packages/solidjs-css/dist/index.cjs +399 -0
- package/packages/solidjs-css/dist/index.cjs.map +1 -0
- package/packages/solidjs-css/{src/seo.css → dist/index.css} +7 -153
- package/packages/solidjs-css/dist/index.css.map +1 -0
- package/packages/solidjs-css/dist/index.d.cts +53 -0
- package/packages/solidjs-css/dist/index.d.ts +53 -0
- package/packages/solidjs-css/dist/index.js +367 -0
- package/packages/solidjs-css/dist/index.js.map +1 -0
- package/.changeset/config.json +0 -11
- package/.github/CODEOWNERS +0 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/ci.yml +0 -23
- package/.github/workflows/release.yml +0 -29
- package/.nvmrc +0 -1
- package/.project/ACCOUNT.yaml +0 -4
- package/.project/IDEAS.yaml +0 -7
- package/.project/PROJECT.yaml +0 -11
- package/.project/ROADMAP.yaml +0 -15
- package/CODE_OF_CONDUCT.md +0 -16
- package/CONTRIBUTING.md +0 -26
- package/SECURITY.md +0 -15
- package/SUPPORT.md +0 -8
- package/packages/convex/package.json +0 -42
- package/packages/convex/src/functions.ts +0 -5
- package/packages/convex/src/mutations.ts +0 -83
- package/packages/convex/src/queries.ts +0 -57
- package/packages/convex/src/schema.ts +0 -23
- package/packages/convex/tsconfig.json +0 -19
- package/packages/convex/tsup.config.ts +0 -18
- package/packages/react/package.json +0 -49
- package/packages/react/src/components/ArticleJsonLd.tsx +0 -42
- package/packages/react/src/components/BreadcrumbsJsonLd.tsx +0 -24
- package/packages/react/src/components/MetaEditor.tsx +0 -147
- package/packages/react/src/components/SEOHead.tsx +0 -107
- package/packages/react/src/components/SEOPreview.tsx +0 -42
- package/packages/react/src/components/SEOScoreCard.tsx +0 -51
- package/packages/react/src/components/SitemapViewer.tsx +0 -36
- package/packages/react/src/components/index.ts +0 -7
- package/packages/react/src/hooks/index.ts +0 -4
- package/packages/react/src/hooks/useSEO.ts +0 -27
- package/packages/react/src/hooks/useSEOAdmin.ts +0 -42
- package/packages/react/src/hooks/useSEOScore.ts +0 -7
- package/packages/react/src/hooks/useSitemap.ts +0 -8
- package/packages/react/src/index.ts +0 -51
- package/packages/react/src/index.tsx +0 -11
- package/packages/react/src/pages/SEOAdminPage.tsx +0 -101
- package/packages/react/src/pages/SEOAnalyticsPage.tsx +0 -96
- package/packages/react/src/pages/index.ts +0 -2
- package/packages/react/tsconfig.json +0 -19
- package/packages/react/tsup.config.ts +0 -12
- package/packages/react-css/package.json +0 -36
- package/packages/react-css/src/components/ArticleJsonLd.tsx +0 -42
- package/packages/react-css/src/components/BreadcrumbsJsonLd.tsx +0 -24
- package/packages/react-css/src/components/MetaEditor.tsx +0 -147
- package/packages/react-css/src/components/SEOHead.tsx +0 -95
- package/packages/react-css/src/components/SEOPreview.tsx +0 -42
- package/packages/react-css/src/components/SEOScoreCard.tsx +0 -42
- package/packages/react-css/src/components/SitemapViewer.tsx +0 -36
- package/packages/react-css/src/components/index.ts +0 -7
- package/packages/react-css/src/index.ts +0 -9
- package/packages/react-css/src/pages/SEOAdminPage.tsx +0 -88
- package/packages/react-css/src/pages/SEOAnalyticsPage.tsx +0 -82
- package/packages/react-css/src/pages/index.ts +0 -2
- package/packages/react-css/tsup.config.ts +0 -2
- package/packages/shared/package.json +0 -42
- package/packages/shared/src/__tests__/seo.test.ts +0 -70
- package/packages/shared/src/config.ts +0 -297
- package/packages/shared/src/index.ts +0 -207
- package/packages/shared/tsconfig.json +0 -18
- package/packages/shared/tsup.config.ts +0 -11
- package/packages/shared/vitest.config.ts +0 -4
- package/packages/solidjs/package.json +0 -45
- package/packages/solidjs/src/components/ArticleJsonLd.tsx +0 -35
- package/packages/solidjs/src/components/BreadcrumbsJsonLd.tsx +0 -24
- package/packages/solidjs/src/components/MetaEditor.tsx +0 -155
- package/packages/solidjs/src/components/SEOHead.tsx +0 -109
- package/packages/solidjs/src/components/SEOPreview.tsx +0 -42
- package/packages/solidjs/src/components/SEOScoreCard.tsx +0 -57
- package/packages/solidjs/src/components/SitemapViewer.tsx +0 -44
- package/packages/solidjs/src/components/index.ts +0 -7
- package/packages/solidjs/src/index.ts +0 -11
- package/packages/solidjs/src/pages/SEOAdminPage.tsx +0 -104
- package/packages/solidjs/src/pages/SEOAnalyticsPage.tsx +0 -102
- package/packages/solidjs/src/pages/index.ts +0 -2
- package/packages/solidjs/src/primitives/index.ts +0 -4
- package/packages/solidjs/src/primitives/useSEO.ts +0 -27
- package/packages/solidjs/src/primitives/useSEOAdmin.ts +0 -42
- package/packages/solidjs/src/primitives/useSEOScore.ts +0 -7
- package/packages/solidjs/src/primitives/useSitemap.ts +0 -8
- package/packages/solidjs/tsconfig.json +0 -20
- package/packages/solidjs/tsup.config.ts +0 -12
- package/packages/solidjs-css/package.json +0 -35
- package/packages/solidjs-css/src/index.ts +0 -5
- package/packages/solidjs-css/src/primitives/index.ts +0 -1
- package/packages/solidjs-css/tsup.config.ts +0 -2
- package/pnpm-workspace.yaml +0 -2
- package/tsconfig.json +0 -23
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { calcSEOScore, generateSitemapXml, buildCanonical, breadcrumbSchema, articleSchema } from '@geenius/seo-shared';
|
|
2
|
+
export * from '@geenius/seo-shared';
|
|
3
|
+
import { createContext, useContext, createSignal, createEffect, createMemo, onCleanup, Show, For } from 'solid-js';
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
var SeoContext = createContext();
|
|
7
|
+
function SeoProvider(props) {
|
|
8
|
+
return /* @__PURE__ */ React.createElement(SeoContext.Provider, { value: { config: props.config } }, props.children);
|
|
9
|
+
}
|
|
10
|
+
function useSEOContext() {
|
|
11
|
+
const ctx = useContext(SeoContext);
|
|
12
|
+
if (!ctx) {
|
|
13
|
+
throw new Error("useSEOContext must be used within a <SeoProvider>");
|
|
14
|
+
}
|
|
15
|
+
return ctx;
|
|
16
|
+
}
|
|
17
|
+
function createSEO(options) {
|
|
18
|
+
const [meta, setMeta] = createSignal(null);
|
|
19
|
+
const [isLoading, setIsLoading] = createSignal(false);
|
|
20
|
+
const updateMeta = (newMeta) => {
|
|
21
|
+
setMeta(newMeta);
|
|
22
|
+
};
|
|
23
|
+
createEffect(() => {
|
|
24
|
+
if (!options?.path) return;
|
|
25
|
+
setIsLoading(true);
|
|
26
|
+
setIsLoading(false);
|
|
27
|
+
});
|
|
28
|
+
return { meta, updateMeta, isLoading };
|
|
29
|
+
}
|
|
30
|
+
var useSEO = createSEO;
|
|
31
|
+
function createSEOAdmin(options) {
|
|
32
|
+
const [pages, setPages] = createSignal([]);
|
|
33
|
+
const [isLoading, setIsLoading] = createSignal(false);
|
|
34
|
+
createEffect(() => {
|
|
35
|
+
const initial = options?.initialPages?.();
|
|
36
|
+
if (initial) {
|
|
37
|
+
setPages(initial);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const upsertMeta = async (path, meta) => {
|
|
41
|
+
if (options?.onUpsert) {
|
|
42
|
+
await options.onUpsert(path, meta);
|
|
43
|
+
}
|
|
44
|
+
setPages((prev) => {
|
|
45
|
+
const existing = prev.findIndex((p) => p.path === path);
|
|
46
|
+
if (existing >= 0) {
|
|
47
|
+
const next = [...prev];
|
|
48
|
+
next[existing] = { path, meta };
|
|
49
|
+
return next;
|
|
50
|
+
}
|
|
51
|
+
return [...prev, { path, meta }];
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const deleteMeta = async (path) => {
|
|
55
|
+
if (options?.onDelete) {
|
|
56
|
+
await options.onDelete(path);
|
|
57
|
+
}
|
|
58
|
+
setPages((prev) => prev.filter((p) => p.path !== path));
|
|
59
|
+
};
|
|
60
|
+
return { pages, upsertMeta, deleteMeta, isLoading };
|
|
61
|
+
}
|
|
62
|
+
var useSEOAdmin = createSEOAdmin;
|
|
63
|
+
function createSEOScore(meta) {
|
|
64
|
+
return createMemo(() => calcSEOScore(meta()));
|
|
65
|
+
}
|
|
66
|
+
var useSEOScore = createSEOScore;
|
|
67
|
+
function createSitemap(entries) {
|
|
68
|
+
const xml = createMemo(() => generateSitemapXml(entries()));
|
|
69
|
+
return { xml };
|
|
70
|
+
}
|
|
71
|
+
var useSitemap = createSitemap;
|
|
72
|
+
function createCanonical(path) {
|
|
73
|
+
const { config } = useSEOContext();
|
|
74
|
+
return createMemo(() => {
|
|
75
|
+
const resolvedPath = path?.() ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
76
|
+
return buildCanonical(resolvedPath, config.siteUrl);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function createStructuredData(data) {
|
|
80
|
+
createEffect(() => {
|
|
81
|
+
const value = data();
|
|
82
|
+
if (!value || Array.isArray(value) && value.length === 0) return;
|
|
83
|
+
const items = Array.isArray(value) ? value : [value];
|
|
84
|
+
const scripts = [];
|
|
85
|
+
for (const item of items) {
|
|
86
|
+
const script = document.createElement("script");
|
|
87
|
+
script.type = "application/ld+json";
|
|
88
|
+
script.textContent = JSON.stringify(item);
|
|
89
|
+
document.head.appendChild(script);
|
|
90
|
+
scripts.push(script);
|
|
91
|
+
}
|
|
92
|
+
onCleanup(() => {
|
|
93
|
+
for (const script of scripts) {
|
|
94
|
+
script.remove();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function SEOHead(props) {
|
|
100
|
+
createEffect(() => {
|
|
101
|
+
const meta = props.meta;
|
|
102
|
+
document.title = meta.title;
|
|
103
|
+
const setMetaTag = (name, content, isProperty = false) => {
|
|
104
|
+
let tag = document.querySelector(
|
|
105
|
+
`meta[${isProperty ? "property" : "name"}="${name}"]`
|
|
106
|
+
);
|
|
107
|
+
if (!tag) {
|
|
108
|
+
tag = document.createElement("meta");
|
|
109
|
+
isProperty ? tag.setAttribute("property", name) : tag.setAttribute("name", name);
|
|
110
|
+
document.head.appendChild(tag);
|
|
111
|
+
}
|
|
112
|
+
tag.content = content;
|
|
113
|
+
};
|
|
114
|
+
setMetaTag("description", meta.description);
|
|
115
|
+
if (meta.keywords.length > 0) {
|
|
116
|
+
setMetaTag("keywords", meta.keywords.join(", "));
|
|
117
|
+
}
|
|
118
|
+
if (meta.robots) {
|
|
119
|
+
setMetaTag("robots", meta.robots);
|
|
120
|
+
}
|
|
121
|
+
setMetaTag("og:title", meta.og.title, true);
|
|
122
|
+
setMetaTag("og:description", meta.og.description, true);
|
|
123
|
+
if (meta.og.image) {
|
|
124
|
+
setMetaTag("og:image", meta.og.image, true);
|
|
125
|
+
if (meta.og.imageAlt) {
|
|
126
|
+
setMetaTag("og:image:alt", meta.og.imageAlt, true);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
setMetaTag("og:type", meta.og.type, true);
|
|
130
|
+
setMetaTag("og:url", meta.og.url, true);
|
|
131
|
+
if (meta.og.siteName) {
|
|
132
|
+
setMetaTag("og:site_name", meta.og.siteName, true);
|
|
133
|
+
}
|
|
134
|
+
setMetaTag("twitter:card", meta.twitter.card);
|
|
135
|
+
setMetaTag("twitter:title", meta.twitter.title);
|
|
136
|
+
setMetaTag("twitter:description", meta.twitter.description);
|
|
137
|
+
if (meta.twitter.image) {
|
|
138
|
+
setMetaTag("twitter:image", meta.twitter.image);
|
|
139
|
+
}
|
|
140
|
+
if (meta.twitter.creator) {
|
|
141
|
+
setMetaTag("twitter:creator", meta.twitter.creator);
|
|
142
|
+
}
|
|
143
|
+
if (meta.canonical) {
|
|
144
|
+
let canonicalLink = document.querySelector('link[rel="canonical"]');
|
|
145
|
+
if (!canonicalLink) {
|
|
146
|
+
canonicalLink = document.createElement("link");
|
|
147
|
+
canonicalLink.rel = "canonical";
|
|
148
|
+
document.head.appendChild(canonicalLink);
|
|
149
|
+
}
|
|
150
|
+
canonicalLink.href = meta.canonical;
|
|
151
|
+
}
|
|
152
|
+
if (meta.alternates) {
|
|
153
|
+
Object.entries(meta.alternates).forEach(([lang, url]) => {
|
|
154
|
+
let altLink = document.querySelector(
|
|
155
|
+
`link[rel="alternate"][hreflang="${lang}"]`
|
|
156
|
+
);
|
|
157
|
+
if (!altLink) {
|
|
158
|
+
altLink = document.createElement("link");
|
|
159
|
+
altLink.rel = "alternate";
|
|
160
|
+
altLink.setAttribute("hreflang", lang);
|
|
161
|
+
document.head.appendChild(altLink);
|
|
162
|
+
}
|
|
163
|
+
altLink.href = url;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
if (meta.jsonLd && meta.jsonLd.length > 0) {
|
|
167
|
+
meta.jsonLd.forEach((schema) => {
|
|
168
|
+
let script = document.querySelector('script[type="application/ld+json"]');
|
|
169
|
+
if (!script) {
|
|
170
|
+
script = document.createElement("script");
|
|
171
|
+
script.type = "application/ld+json";
|
|
172
|
+
document.head.appendChild(script);
|
|
173
|
+
}
|
|
174
|
+
script.textContent = JSON.stringify(schema);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/components/SEOPreview.tsx
|
|
182
|
+
function SEOPreview(props) {
|
|
183
|
+
return /* @__PURE__ */ React.createElement("div", { class: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { class: "space-y-2" }, /* @__PURE__ */ React.createElement("h3", { class: "text-sm font-semibold text-gray-700" }, "Google Search Preview"), /* @__PURE__ */ React.createElement("div", { class: "bg-white p-4 rounded border border-gray-200" }, /* @__PURE__ */ React.createElement("div", { class: "text-xs text-green-700" }, new URL(props.meta.og.url).hostname), /* @__PURE__ */ React.createElement("div", { class: "text-lg text-blue-600 hover:underline cursor-pointer truncate" }, props.meta.title), /* @__PURE__ */ React.createElement("div", { class: "text-sm text-gray-600 line-clamp-2" }, props.meta.description))), /* @__PURE__ */ React.createElement("div", { class: "space-y-2" }, /* @__PURE__ */ React.createElement("h3", { class: "text-sm font-semibold text-gray-700" }, "Social Media Preview"), /* @__PURE__ */ React.createElement("div", { class: "bg-gray-900 rounded overflow-hidden max-w-sm" }, props.meta.og.image && /* @__PURE__ */ React.createElement(
|
|
184
|
+
"img",
|
|
185
|
+
{
|
|
186
|
+
src: props.meta.og.image,
|
|
187
|
+
alt: props.meta.og.imageAlt || props.meta.og.title,
|
|
188
|
+
class: "w-full h-48 object-cover"
|
|
189
|
+
}
|
|
190
|
+
), /* @__PURE__ */ React.createElement("div", { class: "p-3 text-white" }, /* @__PURE__ */ React.createElement("div", { class: "font-semibold text-sm truncate" }, props.meta.og.title), /* @__PURE__ */ React.createElement("div", { class: "text-xs text-gray-400 line-clamp-2" }, props.meta.og.description), /* @__PURE__ */ React.createElement("div", { class: "text-xs text-gray-500 mt-1 truncate" }, props.meta.og.url)))));
|
|
191
|
+
}
|
|
192
|
+
function SEOScoreCard(props) {
|
|
193
|
+
const scoreResult = createSEOScore(() => props.meta);
|
|
194
|
+
const getScoreColor = createMemo(() => {
|
|
195
|
+
const score = scoreResult().score;
|
|
196
|
+
if (score >= 80) return "text-green-600";
|
|
197
|
+
if (score >= 50) return "text-yellow-600";
|
|
198
|
+
return "text-red-600";
|
|
199
|
+
});
|
|
200
|
+
const getBarColor = createMemo(() => {
|
|
201
|
+
const score = scoreResult().score;
|
|
202
|
+
if (score >= 80) return "bg-green-500";
|
|
203
|
+
if (score >= 50) return "bg-yellow-500";
|
|
204
|
+
return "bg-red-500";
|
|
205
|
+
});
|
|
206
|
+
return /* @__PURE__ */ React.createElement("div", { class: "space-y-4 p-4 border border-gray-200 rounded" }, /* @__PURE__ */ React.createElement("div", { class: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("h3", { class: "font-semibold text-gray-900" }, "SEO Score"), /* @__PURE__ */ React.createElement("span", { class: `text-2xl font-bold ${getScoreColor()}` }, scoreResult().score, "/100")), /* @__PURE__ */ React.createElement("div", { class: "space-y-2" }, /* @__PURE__ */ React.createElement("div", { class: "h-2 bg-gray-200 rounded overflow-hidden" }, /* @__PURE__ */ React.createElement(
|
|
207
|
+
"div",
|
|
208
|
+
{
|
|
209
|
+
class: `h-full ${getBarColor()} transition-all`,
|
|
210
|
+
style: { width: `${scoreResult().score}%` }
|
|
211
|
+
}
|
|
212
|
+
))), /* @__PURE__ */ React.createElement(Show, { when: scoreResult().issues.length > 0 }, /* @__PURE__ */ React.createElement("div", { class: "space-y-2" }, /* @__PURE__ */ React.createElement("h4", { class: "text-sm font-semibold text-gray-700" }, "Issues Found"), /* @__PURE__ */ React.createElement("ul", { class: "space-y-1" }, /* @__PURE__ */ React.createElement(For, { each: scoreResult().issues }, (issue) => /* @__PURE__ */ React.createElement("li", { class: "text-sm text-red-600 flex items-start" }, /* @__PURE__ */ React.createElement("span", { class: "mr-2" }, "\u2022"), issue))))));
|
|
213
|
+
}
|
|
214
|
+
function MetaEditor(props) {
|
|
215
|
+
const [localMeta, setLocalMeta] = createSignal(props.meta);
|
|
216
|
+
const handleChange = (updates) => {
|
|
217
|
+
const updated = { ...localMeta(), ...updates };
|
|
218
|
+
setLocalMeta(updated);
|
|
219
|
+
props.onChange(updated);
|
|
220
|
+
};
|
|
221
|
+
return /* @__PURE__ */ React.createElement("div", { class: "space-y-6 p-4 border border-gray-200 rounded" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", { class: "block text-sm font-semibold text-gray-900 mb-1" }, "Title ", localMeta().title.length, "/60"), /* @__PURE__ */ React.createElement(
|
|
222
|
+
"input",
|
|
223
|
+
{
|
|
224
|
+
type: "text",
|
|
225
|
+
value: localMeta().title,
|
|
226
|
+
onInput: (e) => handleChange({
|
|
227
|
+
title: e.currentTarget.value.slice(0, 60)
|
|
228
|
+
}),
|
|
229
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded text-sm",
|
|
230
|
+
placeholder: "Page title"
|
|
231
|
+
}
|
|
232
|
+
), /* @__PURE__ */ React.createElement("p", { class: "text-xs text-gray-500 mt-1" }, localMeta().title.length < 30 ? "Too short" : localMeta().title.length > 60 ? "Too long" : "Good length")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", { class: "block text-sm font-semibold text-gray-900 mb-1" }, "Description ", localMeta().description.length, "/160"), /* @__PURE__ */ React.createElement(
|
|
233
|
+
"textarea",
|
|
234
|
+
{
|
|
235
|
+
value: localMeta().description,
|
|
236
|
+
onInput: (e) => handleChange({
|
|
237
|
+
description: e.currentTarget.value.slice(0, 160)
|
|
238
|
+
}),
|
|
239
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded text-sm",
|
|
240
|
+
rows: 3,
|
|
241
|
+
placeholder: "Page description"
|
|
242
|
+
}
|
|
243
|
+
), /* @__PURE__ */ React.createElement("p", { class: "text-xs text-gray-500 mt-1" }, localMeta().description.length < 50 ? "Too short" : localMeta().description.length > 160 ? "Too long" : "Good length")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", { class: "block text-sm font-semibold text-gray-900 mb-1" }, "Keywords"), /* @__PURE__ */ React.createElement(
|
|
244
|
+
"input",
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
value: localMeta().keywords.join(", "),
|
|
248
|
+
onInput: (e) => handleChange({
|
|
249
|
+
keywords: e.currentTarget.value.split(",").map((k) => k.trim())
|
|
250
|
+
}),
|
|
251
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded text-sm",
|
|
252
|
+
placeholder: "keyword1, keyword2, keyword3"
|
|
253
|
+
}
|
|
254
|
+
)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", { class: "block text-sm font-semibold text-gray-900 mb-1" }, "Canonical URL"), /* @__PURE__ */ React.createElement(
|
|
255
|
+
"input",
|
|
256
|
+
{
|
|
257
|
+
type: "url",
|
|
258
|
+
value: localMeta().canonical || "",
|
|
259
|
+
onInput: (e) => handleChange({
|
|
260
|
+
canonical: e.currentTarget.value
|
|
261
|
+
}),
|
|
262
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded text-sm",
|
|
263
|
+
placeholder: "https://example.com/page"
|
|
264
|
+
}
|
|
265
|
+
)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", { class: "block text-sm font-semibold text-gray-900 mb-1" }, "OG Image URL"), /* @__PURE__ */ React.createElement(
|
|
266
|
+
"input",
|
|
267
|
+
{
|
|
268
|
+
type: "url",
|
|
269
|
+
value: localMeta().og.image || "",
|
|
270
|
+
onInput: (e) => handleChange({
|
|
271
|
+
og: { ...localMeta().og, image: e.currentTarget.value }
|
|
272
|
+
}),
|
|
273
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded text-sm",
|
|
274
|
+
placeholder: "https://example.com/image.jpg"
|
|
275
|
+
}
|
|
276
|
+
)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", { class: "block text-sm font-semibold text-gray-900 mb-1" }, "Twitter Card Type"), /* @__PURE__ */ React.createElement(
|
|
277
|
+
"select",
|
|
278
|
+
{
|
|
279
|
+
value: localMeta().twitter.card,
|
|
280
|
+
onChange: (e) => handleChange({
|
|
281
|
+
twitter: {
|
|
282
|
+
...localMeta().twitter,
|
|
283
|
+
card: e.currentTarget.value
|
|
284
|
+
}
|
|
285
|
+
}),
|
|
286
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded text-sm"
|
|
287
|
+
},
|
|
288
|
+
/* @__PURE__ */ React.createElement("option", { value: "summary" }, "Summary"),
|
|
289
|
+
/* @__PURE__ */ React.createElement("option", { value: "summary_large_image" }, "Summary Large Image")
|
|
290
|
+
)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("label", { class: "block text-sm font-semibold text-gray-900 mb-1" }, "Robots Directive"), /* @__PURE__ */ React.createElement(
|
|
291
|
+
"input",
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
value: localMeta().robots || "",
|
|
295
|
+
onInput: (e) => handleChange({
|
|
296
|
+
robots: e.currentTarget.value
|
|
297
|
+
}),
|
|
298
|
+
class: "w-full px-3 py-2 border border-gray-300 rounded text-sm",
|
|
299
|
+
placeholder: "index, follow"
|
|
300
|
+
}
|
|
301
|
+
)));
|
|
302
|
+
}
|
|
303
|
+
function SitemapViewer(props) {
|
|
304
|
+
return /* @__PURE__ */ React.createElement("div", { class: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { class: "w-full text-sm" }, /* @__PURE__ */ React.createElement("thead", { class: "bg-gray-100 border-b" }, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", { class: "text-left p-3" }, "URL"), /* @__PURE__ */ React.createElement("th", { class: "text-left p-3" }, "Last Modified"), /* @__PURE__ */ React.createElement("th", { class: "text-left p-3" }, "Change Frequency"), /* @__PURE__ */ React.createElement("th", { class: "text-left p-3" }, "Priority"))), /* @__PURE__ */ React.createElement("tbody", null, /* @__PURE__ */ React.createElement(For, { each: props.entries }, (entry, i) => /* @__PURE__ */ React.createElement("tr", { class: "border-b hover:bg-gray-50" }, /* @__PURE__ */ React.createElement("td", { class: "p-3 font-mono text-xs text-blue-600 break-all" }, /* @__PURE__ */ React.createElement(
|
|
305
|
+
"a",
|
|
306
|
+
{
|
|
307
|
+
href: entry.url,
|
|
308
|
+
target: "_blank",
|
|
309
|
+
rel: "noopener noreferrer",
|
|
310
|
+
class: "hover:underline"
|
|
311
|
+
},
|
|
312
|
+
entry.url
|
|
313
|
+
)), /* @__PURE__ */ React.createElement("td", { class: "p-3 text-gray-600" }, entry.lastmod || "-"), /* @__PURE__ */ React.createElement("td", { class: "p-3 text-gray-600" }, entry.changefreq || "-"), /* @__PURE__ */ React.createElement("td", { class: "p-3 text-gray-600" }, entry.priority || "-"))))));
|
|
314
|
+
}
|
|
315
|
+
function BreadcrumbsJsonLd(props) {
|
|
316
|
+
createEffect(() => {
|
|
317
|
+
const script = document.createElement("script");
|
|
318
|
+
script.type = "application/ld+json";
|
|
319
|
+
script.textContent = JSON.stringify(breadcrumbSchema(props.items));
|
|
320
|
+
document.head.appendChild(script);
|
|
321
|
+
return () => {
|
|
322
|
+
document.head.removeChild(script);
|
|
323
|
+
};
|
|
324
|
+
});
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
function ArticleJsonLd(props) {
|
|
328
|
+
createEffect(() => {
|
|
329
|
+
const script = document.createElement("script");
|
|
330
|
+
script.type = "application/ld+json";
|
|
331
|
+
script.textContent = JSON.stringify(
|
|
332
|
+
articleSchema({
|
|
333
|
+
title: props.title,
|
|
334
|
+
description: props.description,
|
|
335
|
+
author: props.author,
|
|
336
|
+
datePublished: props.datePublished,
|
|
337
|
+
url: props.url,
|
|
338
|
+
image: props.image
|
|
339
|
+
})
|
|
340
|
+
);
|
|
341
|
+
document.head.appendChild(script);
|
|
342
|
+
return () => {
|
|
343
|
+
document.head.removeChild(script);
|
|
344
|
+
};
|
|
345
|
+
});
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
function JsonLd(props) {
|
|
349
|
+
const schema = createMemo(() => ({
|
|
350
|
+
"@context": "https://schema.org",
|
|
351
|
+
"@type": props.type,
|
|
352
|
+
...props.data
|
|
353
|
+
}));
|
|
354
|
+
createStructuredData(schema);
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
function SEOAdminPage() {
|
|
358
|
+
const { pages, upsertMeta, deleteMeta, isLoading } = createSEOAdmin();
|
|
359
|
+
const [selectedPath, setSelectedPath] = createSignal(null);
|
|
360
|
+
const [editingMeta, setEditingMeta] = createSignal(null);
|
|
361
|
+
const currentPage = () => pages().find((p) => p.path === selectedPath());
|
|
362
|
+
const handleSave = async () => {
|
|
363
|
+
const path = selectedPath();
|
|
364
|
+
const meta = editingMeta();
|
|
365
|
+
if (path && meta) {
|
|
366
|
+
await upsertMeta(path, meta);
|
|
367
|
+
setEditingMeta(null);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
const handleDelete = async () => {
|
|
371
|
+
const path = selectedPath();
|
|
372
|
+
if (path) {
|
|
373
|
+
await deleteMeta(path);
|
|
374
|
+
setSelectedPath(null);
|
|
375
|
+
setEditingMeta(null);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
return /* @__PURE__ */ React.createElement(Show, { fallback: /* @__PURE__ */ React.createElement("div", { class: "p-4" }, "Loading..."), when: !isLoading() }, /* @__PURE__ */ React.createElement("div", { class: "max-w-6xl mx-auto p-6" }, /* @__PURE__ */ React.createElement("h1", { class: "text-3xl font-bold mb-6" }, "SEO Admin"), /* @__PURE__ */ React.createElement("div", { class: "grid grid-cols-1 lg:grid-cols-4 gap-6" }, /* @__PURE__ */ React.createElement("div", { class: "lg:col-span-1 border border-gray-200 rounded p-4" }, /* @__PURE__ */ React.createElement("h2", { class: "font-semibold mb-4" }, "Pages"), /* @__PURE__ */ React.createElement("div", { class: "space-y-2" }, /* @__PURE__ */ React.createElement(For, { each: pages() }, (page) => /* @__PURE__ */ React.createElement(
|
|
379
|
+
"button",
|
|
380
|
+
{
|
|
381
|
+
onClick: () => {
|
|
382
|
+
setSelectedPath(page.path);
|
|
383
|
+
setEditingMeta(page.meta);
|
|
384
|
+
},
|
|
385
|
+
class: `w-full text-left p-2 rounded text-sm transition ${selectedPath() === page.path ? "bg-blue-100 text-blue-900" : "hover:bg-gray-100"}`
|
|
386
|
+
},
|
|
387
|
+
page.path || "/"
|
|
388
|
+
)))), /* @__PURE__ */ React.createElement(Show, { when: currentPage() && editingMeta() }, (page) => /* @__PURE__ */ React.createElement("div", { class: "lg:col-span-3 space-y-6" }, /* @__PURE__ */ React.createElement("div", { class: "grid grid-cols-1 lg:grid-cols-2 gap-6" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { class: "font-semibold mb-4" }, "Edit Metadata"), /* @__PURE__ */ React.createElement(MetaEditor, { meta: editingMeta(), onChange: setEditingMeta })), /* @__PURE__ */ React.createElement("div", { class: "space-y-6" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { class: "font-semibold mb-4" }, "Preview"), /* @__PURE__ */ React.createElement(SEOPreview, { meta: editingMeta() })), /* @__PURE__ */ React.createElement(SEOScoreCard, { meta: editingMeta() }))), /* @__PURE__ */ React.createElement("div", { class: "flex gap-2" }, /* @__PURE__ */ React.createElement(
|
|
389
|
+
"button",
|
|
390
|
+
{
|
|
391
|
+
onClick: handleSave,
|
|
392
|
+
class: "px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700"
|
|
393
|
+
},
|
|
394
|
+
"Save"
|
|
395
|
+
), /* @__PURE__ */ React.createElement(
|
|
396
|
+
"button",
|
|
397
|
+
{
|
|
398
|
+
onClick: handleDelete,
|
|
399
|
+
class: "px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"
|
|
400
|
+
},
|
|
401
|
+
"Delete"
|
|
402
|
+
)))))));
|
|
403
|
+
}
|
|
404
|
+
function SEOAnalyticsPage() {
|
|
405
|
+
const [topPages, setTopPages] = createSignal([]);
|
|
406
|
+
const [isLoading, setIsLoading] = createSignal(false);
|
|
407
|
+
createEffect(() => {
|
|
408
|
+
setIsLoading(true);
|
|
409
|
+
setIsLoading(false);
|
|
410
|
+
});
|
|
411
|
+
return /* @__PURE__ */ React.createElement(Show, { fallback: /* @__PURE__ */ React.createElement("div", { class: "p-4" }, "Loading..."), when: !isLoading() }, /* @__PURE__ */ React.createElement("div", { class: "max-w-6xl mx-auto p-6" }, /* @__PURE__ */ React.createElement("h1", { class: "text-3xl font-bold mb-6" }, "SEO Analytics"), /* @__PURE__ */ React.createElement("div", { class: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { class: "border border-gray-200 rounded p-4" }, /* @__PURE__ */ React.createElement("h2", { class: "font-semibold mb-4" }, "Top Pages by Views"), /* @__PURE__ */ React.createElement("div", { class: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { class: "w-full text-sm" }, /* @__PURE__ */ React.createElement("thead", { class: "bg-gray-100 border-b" }, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", { class: "text-left p-3" }, "Page"), /* @__PURE__ */ React.createElement("th", { class: "text-right p-3" }, "Views"), /* @__PURE__ */ React.createElement("th", { class: "text-right p-3" }, "Avg Time (s)"), /* @__PURE__ */ React.createElement("th", { class: "text-right p-3" }, "Bounce Rate"))), /* @__PURE__ */ React.createElement("tbody", null, /* @__PURE__ */ React.createElement(For, { each: topPages() }, (page, i) => /* @__PURE__ */ React.createElement("tr", { class: "border-b hover:bg-gray-50" }, /* @__PURE__ */ React.createElement("td", { class: "p-3 font-mono text-xs" }, page.path), /* @__PURE__ */ React.createElement("td", { class: "p-3 text-right" }, page.views.toLocaleString()), /* @__PURE__ */ React.createElement("td", { class: "p-3 text-right" }, page.avgTimeOnPage.toFixed(1)), /* @__PURE__ */ React.createElement("td", { class: "p-3 text-right" }, (page.bounceRate * 100).toFixed(1), "%")))))), /* @__PURE__ */ React.createElement(Show, { when: topPages().length === 0 }, /* @__PURE__ */ React.createElement("p", { class: "text-center text-gray-500 py-8" }, "No analytics data available"))), /* @__PURE__ */ React.createElement("div", { class: "grid grid-cols-1 md:grid-cols-3 gap-4" }, /* @__PURE__ */ React.createElement("div", { class: "border border-gray-200 rounded p-4" }, /* @__PURE__ */ React.createElement("h3", { class: "text-sm text-gray-600 mb-1" }, "Total Views"), /* @__PURE__ */ React.createElement("p", { class: "text-2xl font-bold" }, topPages().reduce((sum, p) => sum + p.views, 0).toLocaleString())), /* @__PURE__ */ React.createElement("div", { class: "border border-gray-200 rounded p-4" }, /* @__PURE__ */ React.createElement("h3", { class: "text-sm text-gray-600 mb-1" }, "Average Time on Page"), /* @__PURE__ */ React.createElement("p", { class: "text-2xl font-bold" }, topPages().length > 0 ? (topPages().reduce((sum, p) => sum + p.avgTimeOnPage, 0) / topPages().length).toFixed(1) : "0", "s")), /* @__PURE__ */ React.createElement("div", { class: "border border-gray-200 rounded p-4" }, /* @__PURE__ */ React.createElement("h3", { class: "text-sm text-gray-600 mb-1" }, "Average Bounce Rate"), /* @__PURE__ */ React.createElement("p", { class: "text-2xl font-bold" }, topPages().length > 0 ? (topPages().reduce((sum, p) => sum + p.bounceRate, 0) / topPages().length * 100).toFixed(1) : "0", "%"))))));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export { ArticleJsonLd, BreadcrumbsJsonLd, JsonLd, MetaEditor, SEOAdminPage, SEOAnalyticsPage, SEOHead, SEOPreview, SEOScoreCard, SeoProvider, SitemapViewer, createCanonical, createSEO, createSEOAdmin, createSEOScore, createSitemap, createStructuredData, useSEO, useSEOAdmin, useSEOContext, useSEOScore, useSitemap };
|
|
415
|
+
//# sourceMappingURL=index.js.map
|
|
416
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider/SeoProvider.tsx","../src/primitives/createSEO.ts","../src/primitives/createSEOAdmin.ts","../src/primitives/createSEOScore.ts","../src/primitives/createSitemap.ts","../src/primitives/createCanonical.ts","../src/primitives/createStructuredData.ts","../src/components/SEOHead.tsx","../src/components/SEOPreview.tsx","../src/components/SEOScoreCard.tsx","../src/components/MetaEditor.tsx","../src/components/SitemapViewer.tsx","../src/components/BreadcrumbsJsonLd.tsx","../src/components/ArticleJsonLd.tsx","../src/components/JsonLd.tsx","../src/pages/SEOAdminPage.tsx","../src/pages/SEOAnalyticsPage.tsx"],"names":["createSignal","createEffect","createMemo","For","Show"],"mappings":";;;;;AAOA,IAAM,aAAa,aAAA,EAA+B;AAO3C,SAAS,YAAY,KAAA,EAAyB;AACnD,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,UAAA,CAAW,QAAA,EAAX,EAAoB,KAAA,EAAO,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAO,EAAA,EAChD,KAAA,CAAM,QACT,CAAA;AAEJ;AAEO,SAAS,aAAA,GAAiC;AAC/C,EAAA,MAAM,GAAA,GAAM,WAAW,UAAU,CAAA;AACjC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,mDAAmD,CAAA;AAAA,EACrE;AACA,EAAA,OAAO,GAAA;AACT;ACrBO,SAAS,UAAU,OAAA,EAA4B;AACpD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,aAA6B,IAAI,CAAA;AACzD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,aAAa,KAAK,CAAA;AAEpD,EAAA,MAAM,UAAA,GAAa,CAAC,OAAA,KAAqB;AACvC,IAAA,OAAA,CAAQ,OAAO,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,YAAA,CAAa,MAAM;AACjB,IAAA,IAAI,CAAC,SAAS,IAAA,EAAM;AAEpB,IAAA,YAAA,CAAa,IAAI,CAAA;AAIjB,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB,CAAC,CAAA;AAED,EAAA,OAAO,EAAE,IAAA,EAAM,UAAA,EAAY,SAAA,EAAU;AACvC;AAGO,IAAM,MAAA,GAAS;ACRf,SAAS,eAAe,OAAA,EAAiC;AAC9D,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,YAAAA,CAA0B,EAAE,CAAA;AACtD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,aAAa,KAAK,CAAA;AAEpD,EAAAC,aAAa,MAAM;AACjB,IAAA,MAAM,OAAA,GAAU,SAAS,YAAA,IAAe;AACxC,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,QAAA,CAAS,OAAO,CAAA;AAAA,IAClB;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAM,UAAA,GAAa,OAAO,IAAA,EAAc,IAAA,KAAkB;AACxD,IAAA,IAAI,SAAS,QAAA,EAAU;AACrB,MAAA,MAAM,OAAA,CAAQ,QAAA,CAAS,IAAA,EAAM,IAAI,CAAA;AAAA,IACnC;AACA,IAAA,QAAA,CAAS,CAAC,IAAA,KAAS;AACjB,MAAA,MAAM,WAAW,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,IAAI,CAAA;AACtD,MAAA,IAAI,YAAY,CAAA,EAAG;AACjB,QAAA,MAAM,IAAA,GAAO,CAAC,GAAG,IAAI,CAAA;AACrB,QAAA,IAAA,CAAK,QAAQ,CAAA,GAAI,EAAE,IAAA,EAAM,IAAA,EAAK;AAC9B,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,OAAO,CAAC,GAAG,IAAA,EAAM,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,IACjC,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,OAAO,IAAA,KAAiB;AACzC,IAAA,IAAI,SAAS,QAAA,EAAU;AACrB,MAAA,MAAM,OAAA,CAAQ,SAAS,IAAI,CAAA;AAAA,IAC7B;AACA,IAAA,QAAA,CAAS,CAAC,SAAS,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAC,CAAA;AAAA,EACxD,CAAA;AAEA,EAAA,OAAO,EAAE,KAAA,EAAO,UAAA,EAAY,UAAA,EAAY,SAAA,EAAU;AACpD;AAGO,IAAM,WAAA,GAAc;ACtDpB,SAAS,eAAe,IAAA,EAAqB;AAClD,EAAA,OAAO,UAAA,CAAW,MAAM,YAAA,CAAa,IAAA,EAAM,CAAC,CAAA;AAC9C;AAGO,IAAM,WAAA,GAAc;ACLpB,SAAS,cAAc,OAAA,EAA+B;AAC3D,EAAA,MAAM,MAAMC,UAAAA,CAAW,MAAM,kBAAA,CAAmB,OAAA,EAAS,CAAC,CAAA;AAC1D,EAAA,OAAO,EAAE,GAAA,EAAI;AACf;AAGO,IAAM,UAAA,GAAa;ACHnB,SAAS,gBAAgB,IAAA,EAAuD;AACrF,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,aAAA,EAAc;AAEjC,EAAA,OAAOA,WAAW,MAAM;AACtB,IAAA,MAAM,YAAA,GAAe,QAAO,KAAM,OAAO,WAAW,WAAA,GAAc,MAAA,CAAO,SAAS,QAAA,GAAW,GAAA,CAAA;AAC7F,IAAA,OAAO,cAAA,CAAe,YAAA,EAAc,MAAA,CAAO,OAAO,CAAA;AAAA,EACpD,CAAC,CAAA;AACH;ACRO,SAAS,qBACd,IAAA,EACM;AACN,EAAAD,aAAa,MAAM;AACjB,IAAA,MAAM,QAAQ,IAAA,EAAK;AACnB,IAAA,IAAI,CAAC,SAAU,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,IAAK,KAAA,CAAM,WAAW,CAAA,EAAI;AAE5D,IAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AACnD,IAAA,MAAM,UAA+B,EAAC;AAEtC,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,MAAA,MAAA,CAAO,IAAA,GAAO,qBAAA;AACd,MAAA,MAAA,CAAO,WAAA,GAAc,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AACxC,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAChC,MAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA,IACrB;AAEA,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,QAAA,MAAA,CAAO,MAAA,EAAO;AAAA,MAChB;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH;ACvBO,SAAS,QAAQ,KAAA,EAAqB;AAC3C,EAAAA,aAAa,MAAM;AACjB,IAAA,MAAM,OAAO,KAAA,CAAM,IAAA;AAGnB,IAAA,QAAA,CAAS,QAAQ,IAAA,CAAK,KAAA;AAGtB,IAAA,MAAM,UAAA,GAAa,CAAC,IAAA,EAAc,OAAA,EAAiB,aAAa,KAAA,KAAU;AACxE,MAAA,IAAI,MAAM,QAAA,CAAS,aAAA;AAAA,QACjB,CAAA,KAAA,EAAQ,UAAA,GAAa,UAAA,GAAa,MAAM,KAAK,IAAI,CAAA,EAAA;AAAA,OACnD;AACA,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA,GAAA,GAAM,QAAA,CAAS,cAAc,MAAM,CAAA;AACnC,QAAA,UAAA,GAAa,GAAA,CAAI,aAAa,UAAA,EAAY,IAAI,IAAI,GAAA,CAAI,YAAA,CAAa,QAAQ,IAAI,CAAA;AAC/E,QAAA,QAAA,CAAS,IAAA,CAAK,YAAY,GAAG,CAAA;AAAA,MAC/B;AACA,MAAA,GAAA,CAAI,OAAA,GAAU,OAAA;AAAA,IAChB,CAAA;AAGA,IAAA,UAAA,CAAW,aAAA,EAAe,KAAK,WAAW,CAAA;AAG1C,IAAA,IAAI,IAAA,CAAK,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG;AAC5B,MAAA,UAAA,CAAW,UAAA,EAAY,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,IACjD;AAGA,IAAA,IAAI,KAAK,MAAA,EAAQ;AACf,MAAA,UAAA,CAAW,QAAA,EAAU,KAAK,MAAM,CAAA;AAAA,IAClC;AAGA,IAAA,UAAA,CAAW,UAAA,EAAY,IAAA,CAAK,EAAA,CAAG,KAAA,EAAO,IAAI,CAAA;AAC1C,IAAA,UAAA,CAAW,gBAAA,EAAkB,IAAA,CAAK,EAAA,CAAG,WAAA,EAAa,IAAI,CAAA;AACtD,IAAA,IAAI,IAAA,CAAK,GAAG,KAAA,EAAO;AACjB,MAAA,UAAA,CAAW,UAAA,EAAY,IAAA,CAAK,EAAA,CAAG,KAAA,EAAO,IAAI,CAAA;AAC1C,MAAA,IAAI,IAAA,CAAK,GAAG,QAAA,EAAU;AACpB,QAAA,UAAA,CAAW,cAAA,EAAgB,IAAA,CAAK,EAAA,CAAG,QAAA,EAAU,IAAI,CAAA;AAAA,MACnD;AAAA,IACF;AACA,IAAA,UAAA,CAAW,SAAA,EAAW,IAAA,CAAK,EAAA,CAAG,IAAA,EAAM,IAAI,CAAA;AACxC,IAAA,UAAA,CAAW,QAAA,EAAU,IAAA,CAAK,EAAA,CAAG,GAAA,EAAK,IAAI,CAAA;AACtC,IAAA,IAAI,IAAA,CAAK,GAAG,QAAA,EAAU;AACpB,MAAA,UAAA,CAAW,cAAA,EAAgB,IAAA,CAAK,EAAA,CAAG,QAAA,EAAU,IAAI,CAAA;AAAA,IACnD;AAGA,IAAA,UAAA,CAAW,cAAA,EAAgB,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAA;AAC5C,IAAA,UAAA,CAAW,eAAA,EAAiB,IAAA,CAAK,OAAA,CAAQ,KAAK,CAAA;AAC9C,IAAA,UAAA,CAAW,qBAAA,EAAuB,IAAA,CAAK,OAAA,CAAQ,WAAW,CAAA;AAC1D,IAAA,IAAI,IAAA,CAAK,QAAQ,KAAA,EAAO;AACtB,MAAA,UAAA,CAAW,eAAA,EAAiB,IAAA,CAAK,OAAA,CAAQ,KAAK,CAAA;AAAA,IAChD;AACA,IAAA,IAAI,IAAA,CAAK,QAAQ,OAAA,EAAS;AACxB,MAAA,UAAA,CAAW,iBAAA,EAAmB,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA;AAAA,IACpD;AAGA,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,IAAI,aAAA,GAAgB,QAAA,CAAS,aAAA,CAAc,uBAAuB,CAAA;AAClE,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA,aAAA,GAAgB,QAAA,CAAS,cAAc,MAAM,CAAA;AAC7C,QAAA,aAAA,CAAc,GAAA,GAAM,WAAA;AACpB,QAAA,QAAA,CAAS,IAAA,CAAK,YAAY,aAAa,CAAA;AAAA,MACzC;AACA,MAAA,aAAA,CAAc,OAAO,IAAA,CAAK,SAAA;AAAA,IAC5B;AAGA,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,MAAA,CAAO,OAAA,CAAQ,KAAK,UAAU,CAAA,CAAE,QAAQ,CAAC,CAAC,IAAA,EAAM,GAAG,CAAA,KAAM;AACvD,QAAA,IAAI,UAAU,QAAA,CAAS,aAAA;AAAA,UACrB,mCAAmC,IAAI,CAAA,EAAA;AAAA,SACzC;AACA,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,OAAA,GAAU,QAAA,CAAS,cAAc,MAAM,CAAA;AACvC,UAAA,OAAA,CAAQ,GAAA,GAAM,WAAA;AACd,UAAA,OAAA,CAAQ,YAAA,CAAa,YAAY,IAAI,CAAA;AACrC,UAAA,QAAA,CAAS,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA,QACnC;AACA,QAAA,OAAA,CAAQ,IAAA,GAAO,GAAA;AAAA,MACjB,CAAC,CAAA;AAAA,IACH;AAGA,IAAA,IAAI,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA,EAAG;AACzC,MAAA,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,CAAC,MAAA,KAAW;AAC9B,QAAA,IAAI,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,oCAAoC,CAAA;AACxE,QAAA,IAAI,CAAC,MAAA,EAAQ;AACX,UAAA,MAAA,GAAS,QAAA,CAAS,cAAc,QAAQ,CAAA;AACxC,UAAA,MAAA,CAAO,IAAA,GAAO,qBAAA;AACd,UAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAAA,QAClC;AACA,QAAA,MAAA,CAAO,WAAA,GAAc,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA;AAAA,MAC5C,CAAC,CAAA;AAAA,IACH;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,IAAA;AACT;;;ACtGO,SAAS,WAAW,KAAA,EAAwB;AACjD,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,WAAA,EAAA,kBAET,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,WAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,qCAAA,EAAA,EAAsC,uBAAqB,CAAA,kBACrE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,6CAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,4BAA0B,IAAI,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,EAAA,CAAG,GAAG,CAAA,CAAE,QAAS,mBACzE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,+DAAA,EAAA,EACR,KAAA,CAAM,IAAA,CAAK,KACd,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,oCAAA,EAAA,EAAsC,KAAA,CAAM,IAAA,CAAK,WAAY,CAC1E,CACF,CAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,WAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,yCAAsC,sBAAoB,CAAA,kBACpE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8CAAA,EAAA,EACR,KAAA,CAAM,IAAA,CAAK,GAAG,KAAA,oBACb,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,KAAA,CAAM,IAAA,CAAK,EAAA,CAAG,KAAA;AAAA,MACnB,KAAK,KAAA,CAAM,IAAA,CAAK,GAAG,QAAA,IAAY,KAAA,CAAM,KAAK,EAAA,CAAG,KAAA;AAAA,MAC7C,KAAA,EAAM;AAAA;AAAA,GACR,kBAEF,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,oCACT,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,gCAAA,EAAA,EAAkC,MAAM,IAAA,CAAK,EAAA,CAAG,KAAM,CAAA,sCAChE,KAAA,EAAA,EAAI,KAAA,EAAM,oCAAA,EAAA,EAAsC,KAAA,CAAM,IAAA,CAAK,EAAA,CAAG,WAAY,CAAA,sCAC1E,KAAA,EAAA,EAAI,KAAA,EAAM,qCAAA,EAAA,EAAuC,KAAA,CAAM,KAAK,EAAA,CAAG,GAAI,CACtE,CACF,CACF,CACF,CAAA;AAEJ;ACjCO,SAAS,aAAa,KAAA,EAA0B;AACrD,EAAA,MAAM,WAAA,GAAc,cAAA,CAAe,MAAM,KAAA,CAAM,IAAI,CAAA;AAEnD,EAAA,MAAM,aAAA,GAAgBC,WAAW,MAAM;AACrC,IAAA,MAAM,KAAA,GAAQ,aAAY,CAAE,KAAA;AAC5B,IAAA,IAAI,KAAA,IAAS,IAAI,OAAO,gBAAA;AACxB,IAAA,IAAI,KAAA,IAAS,IAAI,OAAO,iBAAA;AACxB,IAAA,OAAO,cAAA;AAAA,EACT,CAAC,CAAA;AAED,EAAA,MAAM,WAAA,GAAcA,WAAW,MAAM;AACnC,IAAA,MAAM,KAAA,GAAQ,aAAY,CAAE,KAAA;AAC5B,IAAA,IAAI,KAAA,IAAS,IAAI,OAAO,cAAA;AACxB,IAAA,IAAI,KAAA,IAAS,IAAI,OAAO,eAAA;AACxB,IAAA,OAAO,YAAA;AAAA,EACT,CAAC,CAAA;AAED,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8CAAA,EAAA,sCACR,KAAA,EAAA,EAAI,KAAA,EAAM,mCAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,QAAG,KAAA,EAAM,6BAAA,EAAA,EAA8B,WAAS,CAAA,sCAChD,MAAA,EAAA,EAAK,KAAA,EAAO,CAAA,mBAAA,EAAsB,aAAA,EAAe,CAAA,CAAA,EAAA,EAAK,WAAA,EAAY,CAAE,OAAM,MAAI,CACjF,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAM,WAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAM,yCAAA,EAAA,kBACT,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,OAAA,EAAU,WAAA,EAAa,CAAA,eAAA,CAAA;AAAA,MAC9B,OAAO,EAAE,KAAA,EAAO,GAAG,WAAA,EAAY,CAAE,KAAK,CAAA,CAAA,CAAA;AAAI;AAAA,GAE9C,CACF,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,QAAK,IAAA,EAAM,WAAA,EAAY,CAAE,MAAA,CAAO,MAAA,GAAS,CAAA,EAAA,sCACvC,KAAA,EAAA,EAAI,KAAA,EAAM,+BACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,OAAM,qCAAA,EAAA,EAAsC,cAAY,CAAA,kBAC5D,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,+BACR,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,MAAM,WAAA,EAAY,CAAE,UACtB,CAAC,KAAA,qBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,uCAAA,EAAA,sCACP,MAAA,EAAA,EAAK,KAAA,EAAM,UAAO,QAAC,CAAA,EACnB,KACH,CAEJ,CACF,CACF,CACF,CACF,CAAA;AAEJ;AClDO,SAAS,WAAW,KAAA,EAAwB;AACjD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIF,YAAAA,CAAa,MAAM,IAAI,CAAA;AAEzD,EAAA,MAAM,YAAA,GAAe,CAAC,OAAA,KAA8B;AAClD,IAAA,MAAM,UAAU,EAAE,GAAG,SAAA,EAAU,EAAG,GAAG,OAAA,EAAQ;AAC7C,IAAA,YAAA,CAAa,OAAO,CAAA;AACpB,IAAA,KAAA,CAAM,SAAS,OAAO,CAAA;AAAA,EACxB,CAAA;AAEA,EAAA,2CACG,KAAA,EAAA,EAAI,KAAA,EAAM,8CAAA,EAAA,kBAET,KAAA,CAAA,aAAA,CAAC,6BACC,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAM,gDAAA,EAAA,EAAiD,UACrD,SAAA,EAAU,CAAE,KAAA,CAAM,MAAA,EAAO,KAClC,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,MAAA;AAAA,MACL,KAAA,EAAO,WAAU,CAAE,KAAA;AAAA,MACnB,OAAA,EAAS,CAAC,CAAA,KACR,YAAA,CAAa;AAAA,QACX,OAAO,CAAA,CAAE,aAAA,CAAc,KAAA,CAAM,KAAA,CAAM,GAAG,EAAE;AAAA,OACzC,CAAA;AAAA,MAEH,KAAA,EAAM,yDAAA;AAAA,MACN,WAAA,EAAY;AAAA;AAAA,GACd,kBACA,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAM,gCACN,SAAA,EAAU,CAAE,KAAA,CAAM,MAAA,GAAS,EAAA,GACxB,WAAA,GACA,SAAA,EAAU,CAAE,MAAM,MAAA,GAAS,EAAA,GACzB,UAAA,GACA,aACR,CACF,CAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,sCACE,OAAA,EAAA,EAAM,KAAA,EAAM,gDAAA,EAAA,EAAiD,cAAA,EAC/C,SAAA,EAAU,CAAE,WAAA,CAAY,MAAA,EAAO,MAC9C,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,WAAU,CAAE,WAAA;AAAA,MACnB,OAAA,EAAS,CAAC,CAAA,KACR,YAAA,CAAa;AAAA,QACX,aAAa,CAAA,CAAE,aAAA,CAAc,KAAA,CAAM,KAAA,CAAM,GAAG,GAAG;AAAA,OAChD,CAAA;AAAA,MAEH,KAAA,EAAM,yDAAA;AAAA,MACN,IAAA,EAAM,CAAA;AAAA,MACN,WAAA,EAAY;AAAA;AAAA,GACd,kBACA,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAM,4BAAA,EAAA,EACN,SAAA,EAAU,CAAE,WAAA,CAAY,MAAA,GAAS,EAAA,GAC9B,WAAA,GACA,SAAA,EAAU,CAAE,WAAA,CAAY,MAAA,GAAS,GAAA,GAC/B,UAAA,GACA,aACR,CACF,CAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAM,gDAAA,EAAA,EAAiD,UAAQ,CAAA,kBACtE,KAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,MAAA;AAAA,MACL,KAAA,EAAO,SAAA,EAAU,CAAE,QAAA,CAAS,KAAK,IAAI,CAAA;AAAA,MACrC,OAAA,EAAS,CAAC,CAAA,KACR,YAAA,CAAa;AAAA,QACX,QAAA,EAAU,CAAA,CAAE,aAAA,CAAc,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM;AAAA,OAC/D,CAAA;AAAA,MAEH,KAAA,EAAM,yDAAA;AAAA,MACN,WAAA,EAAY;AAAA;AAAA,GAEhB,mBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,sCACE,OAAA,EAAA,EAAM,KAAA,EAAM,gDAAA,EAAA,EAAiD,eAAa,CAAA,kBAC3E,KAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,KAAA;AAAA,MACL,KAAA,EAAO,SAAA,EAAU,CAAE,SAAA,IAAa,EAAA;AAAA,MAChC,OAAA,EAAS,CAAC,CAAA,KACR,YAAA,CAAa;AAAA,QACX,SAAA,EAAW,EAAE,aAAA,CAAc;AAAA,OAC5B,CAAA;AAAA,MAEH,KAAA,EAAM,yDAAA;AAAA,MACN,WAAA,EAAY;AAAA;AAAA,GAEhB,mBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,sCACE,OAAA,EAAA,EAAM,KAAA,EAAM,gDAAA,EAAA,EAAiD,cAAY,CAAA,kBAC1E,KAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,KAAA;AAAA,MACL,KAAA,EAAO,SAAA,EAAU,CAAE,EAAA,CAAG,KAAA,IAAS,EAAA;AAAA,MAC/B,OAAA,EAAS,CAAC,CAAA,KACR,YAAA,CAAa;AAAA,QACX,EAAA,EAAI,EAAE,GAAG,SAAA,GAAY,EAAA,EAAI,KAAA,EAAO,CAAA,CAAE,aAAA,CAAc,KAAA;AAAM,OACvD,CAAA;AAAA,MAEH,KAAA,EAAM,yDAAA;AAAA,MACN,WAAA,EAAY;AAAA;AAAA,GAEhB,mBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,sCACE,OAAA,EAAA,EAAM,KAAA,EAAM,gDAAA,EAAA,EAAiD,mBAAiB,CAAA,kBAC/E,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,SAAA,EAAU,CAAE,OAAA,CAAQ,IAAA;AAAA,MAC3B,QAAA,EAAU,CAAC,CAAA,KACT,YAAA,CAAa;AAAA,QACX,OAAA,EAAS;AAAA,UACP,GAAG,WAAU,CAAE,OAAA;AAAA,UACf,IAAA,EAAM,EAAE,aAAA,CAAc;AAAA;AACxB,OACD,CAAA;AAAA,MAEH,KAAA,EAAM;AAAA,KAAA;AAAA,oBAEN,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,SAAA,EAAA,EAAU,SAAO,CAAA;AAAA,oBAC/B,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,qBAAA,EAAA,EAAsB,qBAAmB;AAAA,GAE3D,mBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,sCACE,OAAA,EAAA,EAAM,KAAA,EAAM,gDAAA,EAAA,EAAiD,kBAAgB,CAAA,kBAC9E,KAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,MAAA;AAAA,MACL,KAAA,EAAO,SAAA,EAAU,CAAE,MAAA,IAAU,EAAA;AAAA,MAC7B,OAAA,EAAS,CAAC,CAAA,KACR,YAAA,CAAa;AAAA,QACX,MAAA,EAAQ,EAAE,aAAA,CAAc;AAAA,OACzB,CAAA;AAAA,MAEH,KAAA,EAAM,yDAAA;AAAA,MACN,WAAA,EAAY;AAAA;AAAA,GAEhB,CACF,CAAA;AAEJ;ACnJO,SAAS,cAAc,KAAA,EAA2B;AACvD,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,iBAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAM,gBAAA,EAAA,kBACX,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAM,sBAAA,EAAA,kBACX,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,eAAA,EAAA,EAAgB,KAAG,CAAA,kBAC7B,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,eAAA,EAAA,EAAgB,eAAa,CAAA,kBACvC,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,mBAAgB,kBAAgB,CAAA,kBAC1C,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,eAAA,EAAA,EAAgB,UAAQ,CACpC,CACF,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAACG,GAAAA,EAAA,EAAI,IAAA,EAAM,KAAA,CAAM,OAAA,EAAA,EACd,CAAC,KAAA,EAAO,CAAA,qBACP,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,2BAAA,EAAA,kBACR,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,+CAAA,EAAA,kBACR,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,MAAM,KAAA,CAAM,GAAA;AAAA,MACZ,MAAA,EAAO,QAAA;AAAA,MACP,GAAA,EAAI,qBAAA;AAAA,MACJ,KAAA,EAAM;AAAA,KAAA;AAAA,IAEL,KAAA,CAAM;AAAA,GAEX,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,mBAAA,EAAA,EAAqB,KAAA,CAAM,OAAA,IAAW,GAAI,CAAA,kBACpD,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,mBAAA,EAAA,EAAqB,KAAA,CAAM,UAAA,IAAc,GAAI,CAAA,kBACvD,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,mBAAA,EAAA,EAAqB,KAAA,CAAM,QAAA,IAAY,GAAI,CACvD,CAEJ,CACF,CACF,CACF,CAAA;AAEJ;ACjCO,SAAS,kBAAkB,KAAA,EAA+B;AAC/D,EAAAF,aAAa,MAAM;AACjB,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,IAAA,GAAO,qBAAA;AACd,IAAA,MAAA,CAAO,cAAc,IAAA,CAAK,SAAA,CAAU,gBAAA,CAAiB,KAAA,CAAM,KAAK,CAAC,CAAA;AACjE,IAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAEhC,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAAA,IAClC,CAAA;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,IAAA;AACT;ACXO,SAAS,cAAc,KAAA,EAA2B;AACvD,EAAAA,aAAa,MAAM;AACjB,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,IAAA,GAAO,qBAAA;AACd,IAAA,MAAA,CAAO,cAAc,IAAA,CAAK,SAAA;AAAA,MACxB,aAAA,CAAc;AAAA,QACZ,OAAO,KAAA,CAAM,KAAA;AAAA,QACb,aAAa,KAAA,CAAM,WAAA;AAAA,QACnB,QAAQ,KAAA,CAAM,MAAA;AAAA,QACd,eAAe,KAAA,CAAM,aAAA;AAAA,QACrB,KAAK,KAAA,CAAM,GAAA;AAAA,QACX,OAAO,KAAA,CAAM;AAAA,OACd;AAAA,KACH;AACA,IAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAEhC,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAAA,IAClC,CAAA;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,IAAA;AACT;ACvBO,SAAS,OAAO,KAAA,EAAoB;AACzC,EAAA,MAAM,MAAA,GAASC,WAAW,OAAO;AAAA,IAC/B,UAAA,EAAY,oBAAA;AAAA,IACZ,SAAS,KAAA,CAAM,IAAA;AAAA,IACf,GAAG,KAAA,CAAM;AAAA,GACX,CAAE,CAAA;AAEF,EAAA,oBAAA,CAAqB,MAAM,CAAA;AAE3B,EAAA,OAAO,IAAA;AACT;ACdO,SAAS,YAAA,GAAe;AAC7B,EAAA,MAAM,EAAE,KAAA,EAAO,UAAA,EAAY,UAAA,EAAY,SAAA,KAAc,cAAA,EAAe;AACpE,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAIF,aAA4B,IAAI,CAAA;AACxE,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIA,aAA6B,IAAI,CAAA;AAEvE,EAAA,MAAM,WAAA,GAAc,MAAM,KAAA,EAAM,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,YAAA,EAAc,CAAA;AAEvE,EAAA,MAAM,aAAa,YAAY;AAC7B,IAAA,MAAM,OAAO,YAAA,EAAa;AAC1B,IAAA,MAAM,OAAO,WAAA,EAAY;AACzB,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,MAAM,UAAA,CAAW,MAAM,IAAI,CAAA;AAC3B,MAAA,cAAA,CAAe,IAAI,CAAA;AAAA,IACrB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,eAAe,YAAY;AAC/B,IAAA,MAAM,OAAO,YAAA,EAAa;AAC1B,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,MAAM,WAAW,IAAI,CAAA;AACrB,MAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,MAAA,cAAA,CAAe,IAAI,CAAA;AAAA,IACrB;AAAA,EACF,CAAA;AAEA,EAAA,uBACE,KAAA,CAAA,aAAA,CAACI,MAAA,EAAK,QAAA,sCAAW,KAAA,EAAA,EAAI,KAAA,EAAM,KAAA,EAAA,EAAM,YAAU,CAAA,EAAQ,IAAA,EAAM,CAAC,SAAA,EAAU,EAAA,sCACjE,KAAA,EAAA,EAAI,KAAA,EAAM,2CACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,yBAAA,EAAA,EAA0B,WAAS,CAAA,sCAE5C,KAAA,EAAA,EAAI,KAAA,EAAM,2DAET,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAM,kDAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,oBAAA,EAAA,EAAqB,OAAK,mBACpC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,WAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAACD,GAAAA,EAAA,EAAI,IAAA,EAAM,KAAA,EAAM,EAAA,EACd,CAAC,IAAA,qBACA,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,SAAS,MAAM;AACb,QAAA,eAAA,CAAgB,KAAK,IAAI,CAAA;AACzB,QAAA,cAAA,CAAe,KAAK,IAAI,CAAA;AAAA,MAC1B,CAAA;AAAA,MACA,OAAO,CAAA,gDAAA,EACL,YAAA,OAAmB,IAAA,CAAK,IAAA,GACpB,8BACA,mBACN,CAAA;AAAA,KAAA;AAAA,IAEC,KAAK,IAAA,IAAQ;AAAA,GAGpB,CACF,CACF,CAAA,sCAGCC,IAAAA,EAAA,EAAK,IAAA,EAAM,WAAA,EAAY,IAAK,WAAA,EAAY,EAAA,EACtC,CAAC,yBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,yBAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,uCAAA,EAAA,sCACR,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,oBAAA,EAAA,EAAqB,eAAa,CAAA,kBAC5C,KAAA,CAAA,aAAA,CAAC,cAAW,IAAA,EAAM,WAAA,EAAY,EAAI,QAAA,EAAU,cAAA,EAAgB,CAC9D,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAM,WAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,oBAAA,EAAA,EAAqB,SAAO,CAAA,kBACtC,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,IAAA,EAAM,WAAA,EAAY,EAAI,CACpC,CAAA,sCAEC,YAAA,EAAA,EAAa,IAAA,EAAM,WAAA,EAAY,EAAI,CACtC,CACF,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,OAAM,YAAA,EAAA,kBACT,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAS,UAAA;AAAA,MACT,KAAA,EAAM;AAAA,KAAA;AAAA,IACP;AAAA,GAED,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAS,YAAA;AAAA,MACT,KAAA,EAAM;AAAA,KAAA;AAAA,IACP;AAAA,GAGH,CACF,CAEJ,CACF,CACF,CACF,CAAA;AAEJ;AC9FO,SAAS,gBAAA,GAAmB;AACjC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIJ,YAAAA,CAAiC,EAAE,CAAA;AACnE,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,aAAa,KAAK,CAAA;AAEpD,EAAAC,aAAa,MAAM;AACjB,IAAA,YAAA,CAAa,IAAI,CAAA;AAIjB,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB,CAAC,CAAA;AAED,EAAA,uBACE,KAAA,CAAA,aAAA,CAACG,IAAAA,EAAA,EAAK,QAAA,kBAAU,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,KAAA,EAAA,EAAM,YAAU,CAAA,EAAQ,IAAA,EAAM,CAAC,SAAA,EAAU,EAAA,kBAClE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,uBAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,yBAAA,EAAA,EAA0B,eAAa,CAAA,kBAEjD,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,WAAA,EAAA,sCAER,KAAA,EAAA,EAAI,KAAA,EAAM,oCAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,oBAAA,EAAA,EAAqB,oBAAkB,CAAA,kBAEjD,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,iBAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAM,oCACX,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAM,sBAAA,EAAA,kBACX,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,eAAA,EAAA,EAAgB,MAAI,CAAA,kBAC9B,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,gBAAA,EAAA,EAAiB,OAAK,CAAA,kBAChC,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,gBAAA,EAAA,EAAiB,cAAY,CAAA,kBACvC,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,gBAAA,EAAA,EAAiB,aAAW,CACxC,CACF,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,+BACC,KAAA,CAAA,aAAA,CAACD,GAAAA,EAAA,EAAI,IAAA,EAAM,QAAA,EAAS,EAAA,EACjB,CAAC,IAAA,EAAM,CAAA,qBACN,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,2BAAA,EAAA,kBACR,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,2BAAyB,IAAA,CAAK,IAAK,CAAA,kBAC7C,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,gBAAA,EAAA,EAAkB,IAAA,CAAK,KAAA,CAAM,cAAA,EAAiB,CAAA,kBACxD,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,gBAAA,EAAA,EAAkB,KAAK,aAAA,CAAc,OAAA,CAAQ,CAAC,CAAE,CAAA,kBAC1D,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,gBAAA,EAAA,EAAA,CAAmB,IAAA,CAAK,UAAA,GAAa,GAAA,EAAK,OAAA,CAAQ,CAAC,CAAA,EAAE,GAAC,CAClE,CAEJ,CACF,CACF,CACF,CAAA,kBAEA,KAAA,CAAA,aAAA,CAACC,IAAAA,EAAA,EAAK,IAAA,EAAM,QAAA,EAAS,CAAE,MAAA,KAAW,CAAA,EAAA,kBAChC,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAM,gCAAA,EAAA,EAAiC,6BAA2B,CACvE,CACF,CAAA,kBAGA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,uCAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,oCAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,gCAA6B,aAAW,CAAA,kBAClD,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAM,oBAAA,EAAA,EACN,QAAA,EAAS,CACP,MAAA,CAAO,CAAC,GAAA,EAAK,CAAA,KAAM,GAAA,GAAM,CAAA,CAAE,KAAA,EAAO,CAAC,EACnC,cAAA,EACL,CACF,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,oCAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,4BAAA,EAAA,EAA6B,sBAAoB,CAAA,kBAC3D,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,OAAM,oBAAA,EAAA,EACN,QAAA,EAAS,CAAE,MAAA,GAAS,CAAA,GAAA,CAEf,QAAA,EAAS,CAAE,MAAA,CAAO,CAAC,GAAA,EAAK,CAAA,KAAM,GAAA,GAAM,CAAA,CAAE,aAAA,EAAe,CAAC,CAAA,GAAI,UAAS,CAAE,MAAA,EACrE,OAAA,CAAQ,CAAC,CAAA,GACX,GAAA,EAAI,GAEV,CACF,CAAA,kBAEA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,oCAAA,EAAA,kBACT,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAM,gCAA6B,qBAAmB,CAAA,kBAC1D,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAM,oBAAA,EAAA,EACN,QAAA,EAAS,CAAE,MAAA,GAAS,CAAA,GAAA,CAEd,QAAA,EAAS,CAAE,MAAA,CAAO,CAAC,GAAA,EAAK,CAAA,KAAM,MAAM,CAAA,CAAE,UAAA,EAAY,CAAC,CAAA,GAAI,QAAA,EAAS,CAAE,MAAA,GACnE,GAAA,EACA,OAAA,CAAQ,CAAC,CAAA,GACX,GAAA,EAAI,GAEV,CACF,CACF,CACF,CACF,CACF,CAAA;AAEJ","file":"index.js","sourcesContent":["import { createContext, useContext, type JSX } from 'solid-js'\nimport type { SEOConfig } from '@geenius/seo-shared'\n\ninterface SeoContextValue {\n config: SEOConfig\n}\n\nconst SeoContext = createContext<SeoContextValue>()\n\nexport interface SeoProviderProps {\n config: SEOConfig\n children: JSX.Element\n}\n\nexport function SeoProvider(props: SeoProviderProps) {\n return (\n <SeoContext.Provider value={{ config: props.config }}>\n {props.children}\n </SeoContext.Provider>\n )\n}\n\nexport function useSEOContext(): SeoContextValue {\n const ctx = useContext(SeoContext)\n if (!ctx) {\n throw new Error('useSEOContext must be used within a <SeoProvider>')\n }\n return ctx\n}\n","import { createSignal, createEffect } from 'solid-js'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\ninterface CreateSEOOptions {\n path?: string\n}\n\nexport function createSEO(options?: CreateSEOOptions) {\n const [meta, setMeta] = createSignal<SEOMeta | null>(null)\n const [isLoading, setIsLoading] = createSignal(false)\n\n const updateMeta = (newMeta: SEOMeta) => {\n setMeta(newMeta)\n }\n\n createEffect(() => {\n if (!options?.path) return\n\n setIsLoading(true)\n // Placeholder for Convex query integration\n // const meta = await client.query(api.queries.getSEOMeta, { path: options.path })\n // setMeta(meta)\n setIsLoading(false)\n })\n\n return { meta, updateMeta, isLoading }\n}\n\n/** @deprecated Use `createSEO` instead */\nexport const useSEO = createSEO\n","import { createSignal, createEffect, type Accessor } from 'solid-js'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\ninterface PageEntry {\n path: string\n meta: SEOMeta\n}\n\nexport interface CreateSEOAdminOptions {\n /** Pre-loaded pages (e.g. from Convex createQuery) */\n initialPages?: Accessor<PageEntry[] | undefined>\n /** Callback when a page is upserted */\n onUpsert?: (path: string, meta: SEOMeta) => Promise<void>\n /** Callback when a page is deleted */\n onDelete?: (path: string) => Promise<void>\n}\n\n/**\n * Primitive to manage multiple page SEO metadata entries.\n * Supports optional Convex backend via callback props.\n */\nexport function createSEOAdmin(options?: CreateSEOAdminOptions) {\n const [pages, setPages] = createSignal<PageEntry[]>([])\n const [isLoading, setIsLoading] = createSignal(false)\n\n createEffect(() => {\n const initial = options?.initialPages?.()\n if (initial) {\n setPages(initial)\n }\n })\n\n const upsertMeta = async (path: string, meta: SEOMeta) => {\n if (options?.onUpsert) {\n await options.onUpsert(path, meta)\n }\n setPages((prev) => {\n const existing = prev.findIndex((p) => p.path === path)\n if (existing >= 0) {\n const next = [...prev]\n next[existing] = { path, meta }\n return next\n }\n return [...prev, { path, meta }]\n })\n }\n\n const deleteMeta = async (path: string) => {\n if (options?.onDelete) {\n await options.onDelete(path)\n }\n setPages((prev) => prev.filter((p) => p.path !== path))\n }\n\n return { pages, upsertMeta, deleteMeta, isLoading }\n}\n\n/** @deprecated Use `createSEOAdmin` instead */\nexport const useSEOAdmin = createSEOAdmin\n","import { createMemo } from 'solid-js'\nimport { calcSEOScore } from '@geenius/seo-shared'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\nexport function createSEOScore(meta: () => SEOMeta) {\n return createMemo(() => calcSEOScore(meta()))\n}\n\n/** @deprecated Use `createSEOScore` instead */\nexport const useSEOScore = createSEOScore\n","import { createMemo } from 'solid-js'\nimport { generateSitemapXml } from '@geenius/seo-shared'\nimport type { SitemapEntry } from '@geenius/seo-shared'\n\nexport function createSitemap(entries: () => SitemapEntry[]) {\n const xml = createMemo(() => generateSitemapXml(entries()))\n return { xml }\n}\n\n/** @deprecated Use `createSitemap` instead */\nexport const useSitemap = createSitemap\n","import { createMemo, type Accessor } from 'solid-js'\nimport { buildCanonical } from '@geenius/seo-shared'\nimport { useSEOContext } from '../provider'\n\n/**\n * Returns a canonical URL for the given path using the site URL from SeoProvider.\n */\nexport function createCanonical(path?: Accessor<string | undefined>): Accessor<string> {\n const { config } = useSEOContext()\n\n return createMemo(() => {\n const resolvedPath = path?.() ?? (typeof window !== 'undefined' ? window.location.pathname : '/')\n return buildCanonical(resolvedPath, config.siteUrl)\n })\n}\n","import { createEffect, onCleanup, type Accessor } from 'solid-js'\n\n/**\n * Injects JSON-LD structured data into the document head.\n * Cleans up on disposal or when data changes.\n */\nexport function createStructuredData(\n data: Accessor<Record<string, unknown> | Record<string, unknown>[] | undefined>,\n): void {\n createEffect(() => {\n const value = data()\n if (!value || (Array.isArray(value) && value.length === 0)) return\n\n const items = Array.isArray(value) ? value : [value]\n const scripts: HTMLScriptElement[] = []\n\n for (const item of items) {\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(item)\n document.head.appendChild(script)\n scripts.push(script)\n }\n\n onCleanup(() => {\n for (const script of scripts) {\n script.remove()\n }\n })\n })\n}\n","import { createEffect } from 'solid-js'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\ninterface SEOHeadProps {\n meta: SEOMeta\n}\n\nexport function SEOHead(props: SEOHeadProps) {\n createEffect(() => {\n const meta = props.meta\n\n // Set title\n document.title = meta.title\n\n // Set meta tags\n const setMetaTag = (name: string, content: string, isProperty = false) => {\n let tag = document.querySelector(\n `meta[${isProperty ? 'property' : 'name'}=\"${name}\"]`,\n ) as HTMLMetaElement\n if (!tag) {\n tag = document.createElement('meta')\n isProperty ? tag.setAttribute('property', name) : tag.setAttribute('name', name)\n document.head.appendChild(tag)\n }\n tag.content = content\n }\n\n // Description\n setMetaTag('description', meta.description)\n\n // Keywords\n if (meta.keywords.length > 0) {\n setMetaTag('keywords', meta.keywords.join(', '))\n }\n\n // Robots\n if (meta.robots) {\n setMetaTag('robots', meta.robots)\n }\n\n // OG tags\n setMetaTag('og:title', meta.og.title, true)\n setMetaTag('og:description', meta.og.description, true)\n if (meta.og.image) {\n setMetaTag('og:image', meta.og.image, true)\n if (meta.og.imageAlt) {\n setMetaTag('og:image:alt', meta.og.imageAlt, true)\n }\n }\n setMetaTag('og:type', meta.og.type, true)\n setMetaTag('og:url', meta.og.url, true)\n if (meta.og.siteName) {\n setMetaTag('og:site_name', meta.og.siteName, true)\n }\n\n // Twitter tags\n setMetaTag('twitter:card', meta.twitter.card)\n setMetaTag('twitter:title', meta.twitter.title)\n setMetaTag('twitter:description', meta.twitter.description)\n if (meta.twitter.image) {\n setMetaTag('twitter:image', meta.twitter.image)\n }\n if (meta.twitter.creator) {\n setMetaTag('twitter:creator', meta.twitter.creator)\n }\n\n // Canonical\n if (meta.canonical) {\n let canonicalLink = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement\n if (!canonicalLink) {\n canonicalLink = document.createElement('link')\n canonicalLink.rel = 'canonical'\n document.head.appendChild(canonicalLink)\n }\n canonicalLink.href = meta.canonical\n }\n\n // Alternates\n if (meta.alternates) {\n Object.entries(meta.alternates).forEach(([lang, url]) => {\n let altLink = document.querySelector(\n `link[rel=\"alternate\"][hreflang=\"${lang}\"]`,\n ) as HTMLLinkElement\n if (!altLink) {\n altLink = document.createElement('link')\n altLink.rel = 'alternate'\n altLink.setAttribute('hreflang', lang)\n document.head.appendChild(altLink)\n }\n altLink.href = url\n })\n }\n\n // JSON-LD\n if (meta.jsonLd && meta.jsonLd.length > 0) {\n meta.jsonLd.forEach((schema) => {\n let script = document.querySelector('script[type=\"application/ld+json\"]') as HTMLScriptElement\n if (!script) {\n script = document.createElement('script')\n script.type = 'application/ld+json'\n document.head.appendChild(script)\n }\n script.textContent = JSON.stringify(schema)\n })\n }\n })\n\n return null\n}\n","import type { SEOMeta } from '@geenius/seo-shared'\n\ninterface SEOPreviewProps {\n meta: SEOMeta\n}\n\nexport function SEOPreview(props: SEOPreviewProps) {\n return (\n <div class=\"space-y-6\">\n {/* Google SERP Preview */}\n <div class=\"space-y-2\">\n <h3 class=\"text-sm font-semibold text-gray-700\">Google Search Preview</h3>\n <div class=\"bg-white p-4 rounded border border-gray-200\">\n <div class=\"text-xs text-green-700\">{new URL(props.meta.og.url).hostname}</div>\n <div class=\"text-lg text-blue-600 hover:underline cursor-pointer truncate\">\n {props.meta.title}\n </div>\n <div class=\"text-sm text-gray-600 line-clamp-2\">{props.meta.description}</div>\n </div>\n </div>\n\n {/* Social Media Preview */}\n <div class=\"space-y-2\">\n <h3 class=\"text-sm font-semibold text-gray-700\">Social Media Preview</h3>\n <div class=\"bg-gray-900 rounded overflow-hidden max-w-sm\">\n {props.meta.og.image && (\n <img\n src={props.meta.og.image}\n alt={props.meta.og.imageAlt || props.meta.og.title}\n class=\"w-full h-48 object-cover\"\n />\n )}\n <div class=\"p-3 text-white\">\n <div class=\"font-semibold text-sm truncate\">{props.meta.og.title}</div>\n <div class=\"text-xs text-gray-400 line-clamp-2\">{props.meta.og.description}</div>\n <div class=\"text-xs text-gray-500 mt-1 truncate\">{props.meta.og.url}</div>\n </div>\n </div>\n </div>\n </div>\n )\n}\n","import { createMemo, Show, For } from 'solid-js'\nimport { createSEOScore } from '../primitives/createSEOScore'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\ninterface SEOScoreCardProps {\n meta: SEOMeta\n}\n\nexport function SEOScoreCard(props: SEOScoreCardProps) {\n const scoreResult = createSEOScore(() => props.meta)\n\n const getScoreColor = createMemo(() => {\n const score = scoreResult().score\n if (score >= 80) return 'text-green-600'\n if (score >= 50) return 'text-yellow-600'\n return 'text-red-600'\n })\n\n const getBarColor = createMemo(() => {\n const score = scoreResult().score\n if (score >= 80) return 'bg-green-500'\n if (score >= 50) return 'bg-yellow-500'\n return 'bg-red-500'\n })\n\n return (\n <div class=\"space-y-4 p-4 border border-gray-200 rounded\">\n <div class=\"flex items-center justify-between\">\n <h3 class=\"font-semibold text-gray-900\">SEO Score</h3>\n <span class={`text-2xl font-bold ${getScoreColor()}`}>{scoreResult().score}/100</span>\n </div>\n\n <div class=\"space-y-2\">\n <div class=\"h-2 bg-gray-200 rounded overflow-hidden\">\n <div\n class={`h-full ${getBarColor()} transition-all`}\n style={{ width: `${scoreResult().score}%` }}\n />\n </div>\n </div>\n\n <Show when={scoreResult().issues.length > 0}>\n <div class=\"space-y-2\">\n <h4 class=\"text-sm font-semibold text-gray-700\">Issues Found</h4>\n <ul class=\"space-y-1\">\n <For each={scoreResult().issues}>\n {(issue) => (\n <li class=\"text-sm text-red-600 flex items-start\">\n <span class=\"mr-2\">•</span>\n {issue}\n </li>\n )}\n </For>\n </ul>\n </div>\n </Show>\n </div>\n )\n}\n","import { createSignal } from 'solid-js'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\ninterface MetaEditorProps {\n meta: SEOMeta\n onChange: (meta: SEOMeta) => void\n}\n\nexport function MetaEditor(props: MetaEditorProps) {\n const [localMeta, setLocalMeta] = createSignal(props.meta)\n\n const handleChange = (updates: Partial<SEOMeta>) => {\n const updated = { ...localMeta(), ...updates }\n setLocalMeta(updated)\n props.onChange(updated)\n }\n\n return (\n <div class=\"space-y-6 p-4 border border-gray-200 rounded\">\n {/* Title */}\n <div>\n <label class=\"block text-sm font-semibold text-gray-900 mb-1\">\n Title {localMeta().title.length}/60\n </label>\n <input\n type=\"text\"\n value={localMeta().title}\n onInput={(e) =>\n handleChange({\n title: e.currentTarget.value.slice(0, 60),\n })\n }\n class=\"w-full px-3 py-2 border border-gray-300 rounded text-sm\"\n placeholder=\"Page title\"\n />\n <p class=\"text-xs text-gray-500 mt-1\">\n {localMeta().title.length < 30\n ? 'Too short'\n : localMeta().title.length > 60\n ? 'Too long'\n : 'Good length'}\n </p>\n </div>\n\n {/* Description */}\n <div>\n <label class=\"block text-sm font-semibold text-gray-900 mb-1\">\n Description {localMeta().description.length}/160\n </label>\n <textarea\n value={localMeta().description}\n onInput={(e) =>\n handleChange({\n description: e.currentTarget.value.slice(0, 160),\n })\n }\n class=\"w-full px-3 py-2 border border-gray-300 rounded text-sm\"\n rows={3}\n placeholder=\"Page description\"\n />\n <p class=\"text-xs text-gray-500 mt-1\">\n {localMeta().description.length < 50\n ? 'Too short'\n : localMeta().description.length > 160\n ? 'Too long'\n : 'Good length'}\n </p>\n </div>\n\n {/* Keywords */}\n <div>\n <label class=\"block text-sm font-semibold text-gray-900 mb-1\">Keywords</label>\n <input\n type=\"text\"\n value={localMeta().keywords.join(', ')}\n onInput={(e) =>\n handleChange({\n keywords: e.currentTarget.value.split(',').map((k) => k.trim()),\n })\n }\n class=\"w-full px-3 py-2 border border-gray-300 rounded text-sm\"\n placeholder=\"keyword1, keyword2, keyword3\"\n />\n </div>\n\n {/* Canonical */}\n <div>\n <label class=\"block text-sm font-semibold text-gray-900 mb-1\">Canonical URL</label>\n <input\n type=\"url\"\n value={localMeta().canonical || ''}\n onInput={(e) =>\n handleChange({\n canonical: e.currentTarget.value,\n })\n }\n class=\"w-full px-3 py-2 border border-gray-300 rounded text-sm\"\n placeholder=\"https://example.com/page\"\n />\n </div>\n\n {/* OG Image */}\n <div>\n <label class=\"block text-sm font-semibold text-gray-900 mb-1\">OG Image URL</label>\n <input\n type=\"url\"\n value={localMeta().og.image || ''}\n onInput={(e) =>\n handleChange({\n og: { ...localMeta().og, image: e.currentTarget.value },\n })\n }\n class=\"w-full px-3 py-2 border border-gray-300 rounded text-sm\"\n placeholder=\"https://example.com/image.jpg\"\n />\n </div>\n\n {/* Twitter Card */}\n <div>\n <label class=\"block text-sm font-semibold text-gray-900 mb-1\">Twitter Card Type</label>\n <select\n value={localMeta().twitter.card}\n onChange={(e) =>\n handleChange({\n twitter: {\n ...localMeta().twitter,\n card: e.currentTarget.value as 'summary' | 'summary_large_image',\n },\n })\n }\n class=\"w-full px-3 py-2 border border-gray-300 rounded text-sm\"\n >\n <option value=\"summary\">Summary</option>\n <option value=\"summary_large_image\">Summary Large Image</option>\n </select>\n </div>\n\n {/* Robots */}\n <div>\n <label class=\"block text-sm font-semibold text-gray-900 mb-1\">Robots Directive</label>\n <input\n type=\"text\"\n value={localMeta().robots || ''}\n onInput={(e) =>\n handleChange({\n robots: e.currentTarget.value,\n })\n }\n class=\"w-full px-3 py-2 border border-gray-300 rounded text-sm\"\n placeholder=\"index, follow\"\n />\n </div>\n </div>\n )\n}\n","import { For } from 'solid-js'\nimport type { SitemapEntry } from '@geenius/seo-shared'\n\ninterface SitemapViewerProps {\n entries: SitemapEntry[]\n}\n\nexport function SitemapViewer(props: SitemapViewerProps) {\n return (\n <div class=\"overflow-x-auto\">\n <table class=\"w-full text-sm\">\n <thead class=\"bg-gray-100 border-b\">\n <tr>\n <th class=\"text-left p-3\">URL</th>\n <th class=\"text-left p-3\">Last Modified</th>\n <th class=\"text-left p-3\">Change Frequency</th>\n <th class=\"text-left p-3\">Priority</th>\n </tr>\n </thead>\n <tbody>\n <For each={props.entries}>\n {(entry, i) => (\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"p-3 font-mono text-xs text-blue-600 break-all\">\n <a\n href={entry.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"hover:underline\"\n >\n {entry.url}\n </a>\n </td>\n <td class=\"p-3 text-gray-600\">{entry.lastmod || '-'}</td>\n <td class=\"p-3 text-gray-600\">{entry.changefreq || '-'}</td>\n <td class=\"p-3 text-gray-600\">{entry.priority || '-'}</td>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n </div>\n )\n}\n","import { createEffect } from 'solid-js'\nimport { breadcrumbSchema } from '@geenius/seo-shared'\n\ninterface BreadcrumbsJsonLdProps {\n items: {\n name: string\n url: string\n }[]\n}\n\nexport function BreadcrumbsJsonLd(props: BreadcrumbsJsonLdProps) {\n createEffect(() => {\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(breadcrumbSchema(props.items))\n document.head.appendChild(script)\n\n return () => {\n document.head.removeChild(script)\n }\n })\n\n return null\n}\n","import { createEffect } from 'solid-js'\nimport { articleSchema } from '@geenius/seo-shared'\n\ninterface ArticleJsonLdProps {\n title: string\n description: string\n author: string\n datePublished: string\n url: string\n image?: string\n}\n\nexport function ArticleJsonLd(props: ArticleJsonLdProps) {\n createEffect(() => {\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(\n articleSchema({\n title: props.title,\n description: props.description,\n author: props.author,\n datePublished: props.datePublished,\n url: props.url,\n image: props.image,\n }),\n )\n document.head.appendChild(script)\n\n return () => {\n document.head.removeChild(script)\n }\n })\n\n return null\n}\n","import { createMemo } from 'solid-js'\nimport { createStructuredData } from '../primitives/createStructuredData'\n\nexport interface JsonLdProps {\n type: string\n data: Record<string, unknown>\n}\n\n/**\n * Renders a JSON-LD structured data script in the document head.\n */\nexport function JsonLd(props: JsonLdProps) {\n const schema = createMemo(() => ({\n '@context': 'https://schema.org',\n '@type': props.type,\n ...props.data,\n }))\n\n createStructuredData(schema)\n\n return null\n}\n","import { createSignal, For, Show } from 'solid-js'\nimport { createSEOAdmin } from '../primitives/createSEOAdmin'\nimport { MetaEditor } from '../components/MetaEditor'\nimport { SEOPreview } from '../components/SEOPreview'\nimport { SEOScoreCard } from '../components/SEOScoreCard'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\nexport function SEOAdminPage() {\n const { pages, upsertMeta, deleteMeta, isLoading } = createSEOAdmin()\n const [selectedPath, setSelectedPath] = createSignal<string | null>(null)\n const [editingMeta, setEditingMeta] = createSignal<SEOMeta | null>(null)\n\n const currentPage = () => pages().find((p) => p.path === selectedPath())\n\n const handleSave = async () => {\n const path = selectedPath()\n const meta = editingMeta()\n if (path && meta) {\n await upsertMeta(path, meta)\n setEditingMeta(null)\n }\n }\n\n const handleDelete = async () => {\n const path = selectedPath()\n if (path) {\n await deleteMeta(path)\n setSelectedPath(null)\n setEditingMeta(null)\n }\n }\n\n return (\n <Show fallback={<div class=\"p-4\">Loading...</div>} when={!isLoading()}>\n <div class=\"max-w-6xl mx-auto p-6\">\n <h1 class=\"text-3xl font-bold mb-6\">SEO Admin</h1>\n\n <div class=\"grid grid-cols-1 lg:grid-cols-4 gap-6\">\n {/* Pages List */}\n <div class=\"lg:col-span-1 border border-gray-200 rounded p-4\">\n <h2 class=\"font-semibold mb-4\">Pages</h2>\n <div class=\"space-y-2\">\n <For each={pages()}>\n {(page) => (\n <button\n onClick={() => {\n setSelectedPath(page.path)\n setEditingMeta(page.meta)\n }}\n class={`w-full text-left p-2 rounded text-sm transition ${\n selectedPath() === page.path\n ? 'bg-blue-100 text-blue-900'\n : 'hover:bg-gray-100'\n }`}\n >\n {page.path || '/'}\n </button>\n )}\n </For>\n </div>\n </div>\n\n {/* Editor and Preview */}\n <Show when={currentPage() && editingMeta()}>\n {(page) => (\n <div class=\"lg:col-span-3 space-y-6\">\n <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <div>\n <h2 class=\"font-semibold mb-4\">Edit Metadata</h2>\n <MetaEditor meta={editingMeta()!} onChange={setEditingMeta} />\n </div>\n\n <div class=\"space-y-6\">\n <div>\n <h2 class=\"font-semibold mb-4\">Preview</h2>\n <SEOPreview meta={editingMeta()!} />\n </div>\n\n <SEOScoreCard meta={editingMeta()!} />\n </div>\n </div>\n\n <div class=\"flex gap-2\">\n <button\n onClick={handleSave}\n class=\"px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700\"\n >\n Save\n </button>\n <button\n onClick={handleDelete}\n class=\"px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700\"\n >\n Delete\n </button>\n </div>\n </div>\n )}\n </Show>\n </div>\n </div>\n </Show>\n )\n}\n","import { createSignal, createEffect, For, Show } from 'solid-js'\n\ninterface AnalyticsSummary {\n path: string\n views: number\n avgTimeOnPage: number\n bounceRate: number\n}\n\nexport function SEOAnalyticsPage() {\n const [topPages, setTopPages] = createSignal<AnalyticsSummary[]>([])\n const [isLoading, setIsLoading] = createSignal(false)\n\n createEffect(() => {\n setIsLoading(true)\n // Placeholder for Convex query integration\n // const result = await client.query(api.queries.getTopPages, { limit: 10 })\n // setTopPages(result)\n setIsLoading(false)\n })\n\n return (\n <Show fallback={<div class=\"p-4\">Loading...</div>} when={!isLoading()}>\n <div class=\"max-w-6xl mx-auto p-6\">\n <h1 class=\"text-3xl font-bold mb-6\">SEO Analytics</h1>\n\n <div class=\"space-y-6\">\n {/* Top Pages */}\n <div class=\"border border-gray-200 rounded p-4\">\n <h2 class=\"font-semibold mb-4\">Top Pages by Views</h2>\n\n <div class=\"overflow-x-auto\">\n <table class=\"w-full text-sm\">\n <thead class=\"bg-gray-100 border-b\">\n <tr>\n <th class=\"text-left p-3\">Page</th>\n <th class=\"text-right p-3\">Views</th>\n <th class=\"text-right p-3\">Avg Time (s)</th>\n <th class=\"text-right p-3\">Bounce Rate</th>\n </tr>\n </thead>\n <tbody>\n <For each={topPages()}>\n {(page, i) => (\n <tr class=\"border-b hover:bg-gray-50\">\n <td class=\"p-3 font-mono text-xs\">{page.path}</td>\n <td class=\"p-3 text-right\">{page.views.toLocaleString()}</td>\n <td class=\"p-3 text-right\">{page.avgTimeOnPage.toFixed(1)}</td>\n <td class=\"p-3 text-right\">{(page.bounceRate * 100).toFixed(1)}%</td>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n </div>\n\n <Show when={topPages().length === 0}>\n <p class=\"text-center text-gray-500 py-8\">No analytics data available</p>\n </Show>\n </div>\n\n {/* Analytics Summary */}\n <div class=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <div class=\"border border-gray-200 rounded p-4\">\n <h3 class=\"text-sm text-gray-600 mb-1\">Total Views</h3>\n <p class=\"text-2xl font-bold\">\n {topPages()\n .reduce((sum, p) => sum + p.views, 0)\n .toLocaleString()}\n </p>\n </div>\n\n <div class=\"border border-gray-200 rounded p-4\">\n <h3 class=\"text-sm text-gray-600 mb-1\">Average Time on Page</h3>\n <p class=\"text-2xl font-bold\">\n {topPages().length > 0\n ? (\n topPages().reduce((sum, p) => sum + p.avgTimeOnPage, 0) / topPages().length\n ).toFixed(1)\n : '0'}\n s\n </p>\n </div>\n\n <div class=\"border border-gray-200 rounded p-4\">\n <h3 class=\"text-sm text-gray-600 mb-1\">Average Bounce Rate</h3>\n <p class=\"text-2xl font-bold\">\n {topPages().length > 0\n ? (\n (topPages().reduce((sum, p) => sum + p.bounceRate, 0) / topPages().length) *\n 100\n ).toFixed(1)\n : '0'}\n %\n </p>\n </div>\n </div>\n </div>\n </div>\n </Show>\n )\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
# ✦ @geenius-
|
|
1
|
+
# ✦ @geenius/seo-solidjs-css\n\n> A premium module for the Geenius Boilerplate Ecosystem.\n\n---\n\n## Overview\nBuilt with Steve Jobs-level minimalism and Jony Ive-level craftsmanship, this package is designed to deliver unparalleled developer experience (DX) and rock-solid performance.\n\n## Installation\n\n```bash\npnpm add @geenius/seo-solidjs-css\n```\n\n## Usage\n\n```typescript\nimport { init } from '@geenius/seo-solidjs-css';\n\n// Initialize the module with absolute precision\ninit({\n mode: 'premium',\n});\n```\n\n## Architecture\n- **Zero-config**: It just works.\n- **Strictly Typed**: Fully written in TypeScript for flawless IntelliSense.\n- **Framework Agnostic**: seamlessly integrates into the Geenius ecosystem.\n\n---\n\n*Designed by Antigravity HQ*\n
|