@usezanin/blog 0.1.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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +68 -0
  3. package/dist/BlogIndex.d.ts +9 -0
  4. package/dist/BlogIndex.d.ts.map +1 -0
  5. package/dist/BlogIndex.js +21 -0
  6. package/dist/BlogIndex.js.map +1 -0
  7. package/dist/BlogPost.d.ts +14 -0
  8. package/dist/BlogPost.d.ts.map +1 -0
  9. package/dist/BlogPost.js +53 -0
  10. package/dist/BlogPost.js.map +1 -0
  11. package/dist/components/CTA.d.ts +7 -0
  12. package/dist/components/CTA.d.ts.map +1 -0
  13. package/dist/components/CTA.js +5 -0
  14. package/dist/components/CTA.js.map +1 -0
  15. package/dist/components/Callout.d.ts +8 -0
  16. package/dist/components/Callout.d.ts.map +1 -0
  17. package/dist/components/Callout.js +11 -0
  18. package/dist/components/Callout.js.map +1 -0
  19. package/dist/components/Chart.d.ts +16 -0
  20. package/dist/components/Chart.d.ts.map +1 -0
  21. package/dist/components/Chart.js +28 -0
  22. package/dist/components/Chart.js.map +1 -0
  23. package/dist/components/CodeBlock.d.ts +9 -0
  24. package/dist/components/CodeBlock.d.ts.map +1 -0
  25. package/dist/components/CodeBlock.js +13 -0
  26. package/dist/components/CodeBlock.js.map +1 -0
  27. package/dist/components/ComparisonTable.d.ts +9 -0
  28. package/dist/components/ComparisonTable.d.ts.map +1 -0
  29. package/dist/components/ComparisonTable.js +12 -0
  30. package/dist/components/ComparisonTable.js.map +1 -0
  31. package/dist/components/Embeds.d.ts +15 -0
  32. package/dist/components/Embeds.d.ts.map +1 -0
  33. package/dist/components/Embeds.js +13 -0
  34. package/dist/components/Embeds.js.map +1 -0
  35. package/dist/components/FAQ.d.ts +9 -0
  36. package/dist/components/FAQ.d.ts.map +1 -0
  37. package/dist/components/FAQ.js +15 -0
  38. package/dist/components/FAQ.js.map +1 -0
  39. package/dist/components/Figure.d.ts +8 -0
  40. package/dist/components/Figure.d.ts.map +1 -0
  41. package/dist/components/Figure.js +5 -0
  42. package/dist/components/Figure.js.map +1 -0
  43. package/dist/config.d.ts +30 -0
  44. package/dist/config.d.ts.map +1 -0
  45. package/dist/config.js +27 -0
  46. package/dist/config.js.map +1 -0
  47. package/dist/custom-components.d.ts +9 -0
  48. package/dist/custom-components.d.ts.map +1 -0
  49. package/dist/custom-components.js +58 -0
  50. package/dist/custom-components.js.map +1 -0
  51. package/dist/data.d.ts +9 -0
  52. package/dist/data.d.ts.map +1 -0
  53. package/dist/data.js +36 -0
  54. package/dist/data.js.map +1 -0
  55. package/dist/handlers.d.ts +21 -0
  56. package/dist/handlers.d.ts.map +1 -0
  57. package/dist/handlers.js +89 -0
  58. package/dist/handlers.js.map +1 -0
  59. package/dist/index.d.ts +18 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +17 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/mdx.d.ts +8 -0
  64. package/dist/mdx.d.ts.map +1 -0
  65. package/dist/mdx.js +35 -0
  66. package/dist/mdx.js.map +1 -0
  67. package/dist/seo.d.ts +19 -0
  68. package/dist/seo.d.ts.map +1 -0
  69. package/dist/seo.js +49 -0
  70. package/dist/seo.js.map +1 -0
  71. package/package.json +61 -0
  72. package/styles.css +484 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Siyabend Oezdemir
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # @usezanin/blog
2
+
3
+ React SDK for rendering a [Zanin](https://github.com/siyabendoezdemir/zanin) blog inside any Next.js (App Router) site. Two page files and you have a fully styled, SEO-complete blog whose content agents manage through the Zanin API/MCP.
4
+
5
+ ```bash
6
+ npm install @usezanin/blog
7
+ ```
8
+
9
+ ## Setup
10
+
11
+ Env vars: `ZANIN_BASE_URL`, `ZANIN_SITE_ID`, `ZANIN_SITE_KEY` (from the Zanin dashboard), optionally `ZANIN_SITE_URL` (your site's public origin, for canonical URLs/RSS) and `ZANIN_REVALIDATE_SECRET`.
12
+
13
+ ```tsx
14
+ // app/layout.tsx — import the themable styles once
15
+ import "@usezanin/blog/styles.css";
16
+ ```
17
+
18
+ ```tsx
19
+ // app/blog/page.tsx
20
+ import { BlogIndex } from "@usezanin/blog";
21
+
22
+ export default function BlogPage() {
23
+ return <BlogIndex title="Blog" description="Notes from the team." />;
24
+ }
25
+ ```
26
+
27
+ ```tsx
28
+ // app/blog/[slug]/page.tsx
29
+ import { BlogPost, generateBlogMetadata, generateBlogStaticParams } from "@usezanin/blog";
30
+
31
+ export async function generateStaticParams() {
32
+ return generateBlogStaticParams();
33
+ }
34
+
35
+ export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
36
+ return generateBlogMetadata((await params).slug);
37
+ }
38
+
39
+ export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
40
+ return <BlogPost slug={(await params).slug} />;
41
+ }
42
+ ```
43
+
44
+ Optional one-line route handlers:
45
+
46
+ ```ts
47
+ // app/blog/rss.xml/route.ts
48
+ import { createRssHandler } from "@usezanin/blog";
49
+ export const GET = createRssHandler();
50
+
51
+ // app/blog/sitemap.xml/route.ts
52
+ import { createSitemapHandler } from "@usezanin/blog";
53
+ export const GET = createSitemapHandler();
54
+
55
+ // app/api/zanin/revalidate/route.ts — point the site's webhook here for instant publishes
56
+ import { createRevalidateHandler } from "@usezanin/blog";
57
+ export const POST = createRevalidateHandler();
58
+ ```
59
+
60
+ ## What you get
61
+
62
+ - Server-rendered MDX with built-in components: `Callout`, `Figure`, `Chart` (bar/line/area/pie from inline data), `CTA`, `YouTube`, `Tweet`, `ComparisonTable`, `FAQ` (with FAQPage JSON-LD), styled code blocks
63
+ - Per-site custom components: agents upload TSX to the CMS; the SDK loads the compiled result at render time — no redeploy
64
+ - SEO: canonical/OG/Twitter metadata, JSON-LD Article schema, static params for SSG, RSS + sitemap
65
+ - ISR with instant, webhook-driven revalidation on publish
66
+ - Theming via `--zanin-*` CSS variables (accent, fonts, measure, radii)
67
+
68
+ MIT
@@ -0,0 +1,9 @@
1
+ import { type ZaninBlogConfig } from "./config.js";
2
+ export interface BlogIndexProps {
3
+ config?: ZaninBlogConfig;
4
+ title?: string;
5
+ description?: string;
6
+ }
7
+ /** Server component: renders the list of published posts. */
8
+ export declare function BlogIndex({ config, title, description }: BlogIndexProps): Promise<import("react").JSX.Element>;
9
+ //# sourceMappingURL=BlogIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogIndex.d.ts","sourceRoot":"","sources":["../src/BlogIndex.tsx"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAiClE,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,6DAA6D;AAC7D,wBAAsB,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,cAAc,wCAqB7E"}
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveConfig } from "./config.js";
3
+ import { fetchPublishedPosts } from "./data.js";
4
+ function formatDate(iso) {
5
+ return new Date(iso).toLocaleDateString("en-US", {
6
+ year: "numeric",
7
+ month: "long",
8
+ day: "numeric",
9
+ });
10
+ }
11
+ function PostCard({ post, basePath }) {
12
+ const href = `${basePath}/${post.slug}`.replace(/\/{2,}/g, "/");
13
+ return (_jsx("article", { className: "zanin-index-item", children: _jsxs("a", { href: href, children: [_jsxs("div", { className: "zanin-index-meta", children: [_jsx("time", { dateTime: post.publishedAt ?? undefined, children: formatDate(post.publishedAt ?? post.createdAt) }), post.tags.slice(0, 3).map((tag) => (_jsx("span", { className: "zanin-tag", children: tag }, tag)))] }), _jsx("h2", { children: post.title }), post.description && _jsx("p", { children: post.description })] }) }));
14
+ }
15
+ /** Server component: renders the list of published posts. */
16
+ export async function BlogIndex({ config, title, description }) {
17
+ const resolved = resolveConfig(config);
18
+ const posts = await fetchPublishedPosts(resolved);
19
+ return (_jsxs("div", { className: "zanin-blog zanin-index", children: [_jsxs("header", { className: "zanin-index-header", children: [_jsx("h1", { children: title ?? "Blog" }), description && _jsx("p", { children: description })] }), posts.length === 0 ? (_jsx("p", { className: "zanin-index-empty", children: "No posts yet." })) : (_jsx("div", { className: "zanin-index-list", children: posts.map((post) => (_jsx(PostCard, { post: post, basePath: resolved.basePath }, post.id))) }))] }));
20
+ }
21
+ //# sourceMappingURL=BlogIndex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogIndex.js","sourceRoot":"","sources":["../src/BlogIndex.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAwB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEhD,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;QAC/C,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,MAAM;QACb,GAAG,EAAE,SAAS;KACf,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAoC;IACpE,MAAM,IAAI,GAAG,GAAG,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAChE,OAAO,CACL,kBAAS,SAAS,EAAC,kBAAkB,YACnC,aAAG,IAAI,EAAE,IAAI,aACX,eAAK,SAAS,EAAC,kBAAkB,aAC/B,eAAM,QAAQ,EAAE,IAAI,CAAC,WAAW,IAAI,SAAS,YAC1C,UAAU,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,SAAS,CAAC,GAC1C,EACN,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAClC,eAAgB,SAAS,EAAC,WAAW,YAClC,GAAG,IADK,GAAG,CAEP,CACR,CAAC,IACE,EACN,uBAAK,IAAI,CAAC,KAAK,GAAM,EACpB,IAAI,CAAC,WAAW,IAAI,sBAAI,IAAI,CAAC,WAAW,GAAK,IAC5C,GACI,CACX,CAAC;AACJ,CAAC;AAQD,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAkB;IAC5E,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAElD,OAAO,CACL,eAAK,SAAS,EAAC,wBAAwB,aACrC,kBAAQ,SAAS,EAAC,oBAAoB,aACpC,uBAAK,KAAK,IAAI,MAAM,GAAM,EACzB,WAAW,IAAI,sBAAI,WAAW,GAAK,IAC7B,EACR,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACpB,YAAG,SAAS,EAAC,mBAAmB,8BAAkB,CACnD,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,kBAAkB,YAC9B,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,KAAC,QAAQ,IAAe,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,IAAhD,IAAI,CAAC,EAAE,CAA6C,CACpE,CAAC,GACE,CACP,IACG,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { MDXComponents } from "mdx/types";
2
+ import { type ZaninBlogConfig } from "./config.js";
3
+ export interface BlogPostProps {
4
+ slug: string;
5
+ config?: ZaninBlogConfig;
6
+ /** Extra/override MDX components merged over the built-ins. */
7
+ components?: MDXComponents;
8
+ }
9
+ /**
10
+ * Server component: fetches a published post by slug and renders it,
11
+ * including JSON-LD Article schema. Calls notFound() for unknown slugs.
12
+ */
13
+ export declare function BlogPost({ slug, config, components }: BlogPostProps): Promise<import("react").JSX.Element>;
14
+ //# sourceMappingURL=BlogPost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogPost.d.ts","sourceRoot":"","sources":["../src/BlogPost.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAiBlE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,aAAa,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,aAAa,wCAyDzE"}
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { notFound } from "next/navigation";
3
+ import { resolveConfig } from "./config.js";
4
+ import { fetchCustomComponents } from "./custom-components.js";
5
+ import { fetchPublishedPost, postUrl } from "./data.js";
6
+ import { renderMdx } from "./mdx.js";
7
+ function formatDate(iso) {
8
+ return new Date(iso).toLocaleDateString("en-US", {
9
+ year: "numeric",
10
+ month: "long",
11
+ day: "numeric",
12
+ });
13
+ }
14
+ function readingTime(body) {
15
+ return Math.max(1, Math.round(body.split(/\s+/).length / 220));
16
+ }
17
+ /**
18
+ * Server component: fetches a published post by slug and renders it,
19
+ * including JSON-LD Article schema. Calls notFound() for unknown slugs.
20
+ */
21
+ export async function BlogPost({ slug, config, components }) {
22
+ const resolved = resolveConfig(config);
23
+ const [post, customComponents] = await Promise.all([
24
+ fetchPublishedPost(resolved, slug),
25
+ fetchCustomComponents(resolved),
26
+ ]);
27
+ if (!post)
28
+ notFound();
29
+ // Precedence: built-ins < site custom components < host overrides.
30
+ const content = await renderMdx(post.body ?? "", {
31
+ ...customComponents,
32
+ ...components,
33
+ });
34
+ const jsonLd = {
35
+ "@context": "https://schema.org",
36
+ "@type": "Article",
37
+ headline: post.title,
38
+ description: post.description ?? undefined,
39
+ datePublished: post.publishedAt ?? undefined,
40
+ dateModified: post.updatedAt,
41
+ image: post.ogImageUrl ?? undefined,
42
+ ...(resolved.siteUrl
43
+ ? {
44
+ mainEntityOfPage: {
45
+ "@type": "WebPage",
46
+ "@id": postUrl(resolved, post.slug),
47
+ },
48
+ }
49
+ : {}),
50
+ };
51
+ return (_jsxs("article", { className: "zanin-blog zanin-post", children: [_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(jsonLd) } }), _jsxs("header", { className: "zanin-post-header", children: [_jsxs("div", { className: "zanin-post-meta", children: [_jsx("time", { dateTime: post.publishedAt ?? undefined, children: formatDate(post.publishedAt ?? post.createdAt) }), _jsx("span", { "aria-hidden": true, children: "\u00B7" }), _jsxs("span", { children: [readingTime(post.body ?? ""), " min read"] }), post.tags.slice(0, 3).map((tag) => (_jsx("span", { className: "zanin-tag", children: tag }, tag)))] }), _jsx("h1", { children: post.title }), post.description && _jsx("p", { className: "zanin-post-lede", children: post.description })] }), _jsx("div", { className: "zanin-prose", children: content })] }));
52
+ }
53
+ //# sourceMappingURL=BlogPost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogPost.js","sourceRoot":"","sources":["../src/BlogPost.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function CTA({ title, description, href, label, }: {
2
+ title: string;
3
+ description?: string;
4
+ href: string;
5
+ label: string;
6
+ }): import("react").JSX.Element;
7
+ //# sourceMappingURL=CTA.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CTA.d.ts","sourceRoot":"","sources":["../../src/components/CTA.tsx"],"names":[],"mappings":"AAAA,wBAAgB,GAAG,CAAC,EAClB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,KAAK,GACN,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,+BAYA"}
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function CTA({ title, description, href, label, }) {
3
+ return (_jsxs("aside", { className: "zanin-cta", children: [_jsxs("div", { children: [_jsx("p", { className: "zanin-cta-title", children: title }), description && _jsx("p", { className: "zanin-cta-description", children: description })] }), _jsx("a", { className: "zanin-cta-button", href: href, children: label })] }));
4
+ }
5
+ //# sourceMappingURL=CTA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CTA.js","sourceRoot":"","sources":["../../src/components/CTA.tsx"],"names":[],"mappings":";AAAA,MAAM,UAAU,GAAG,CAAC,EAClB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,KAAK,GAMN;IACC,OAAO,CACL,iBAAO,SAAS,EAAC,WAAW,aAC1B,0BACE,YAAG,SAAS,EAAC,iBAAiB,YAAE,KAAK,GAAK,EACzC,WAAW,IAAI,YAAG,SAAS,EAAC,uBAAuB,YAAE,WAAW,GAAK,IAClE,EACN,YAAG,SAAS,EAAC,kBAAkB,EAAC,IAAI,EAAE,IAAI,YACvC,KAAK,GACJ,IACE,CACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+ export type CalloutType = "info" | "warning" | "success" | "note";
3
+ export declare function Callout({ type, title, children, }: {
4
+ type?: CalloutType;
5
+ title?: string;
6
+ children: ReactNode;
7
+ }): import("react").JSX.Element;
8
+ //# sourceMappingURL=Callout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Callout.d.ts","sourceRoot":"","sources":["../../src/components/Callout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AASlE,wBAAgB,OAAO,CAAC,EACtB,IAAa,EACb,KAAK,EACL,QAAQ,GACT,EAAE;IACD,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAYA"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const ICONS = {
3
+ info: "i",
4
+ warning: "!",
5
+ success: "✓",
6
+ note: "✎",
7
+ };
8
+ export function Callout({ type = "info", title, children, }) {
9
+ return (_jsxs("aside", { className: `zanin-callout zanin-callout-${type}`, children: [_jsx("span", { className: "zanin-callout-icon", "aria-hidden": true, children: ICONS[type] }), _jsxs("div", { className: "zanin-callout-body", children: [title && _jsx("p", { className: "zanin-callout-title", children: title }), children] })] }));
10
+ }
11
+ //# sourceMappingURL=Callout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Callout.js","sourceRoot":"","sources":["../../src/components/Callout.tsx"],"names":[],"mappings":";AAIA,MAAM,KAAK,GAAgC;IACzC,IAAI,EAAE,GAAG;IACT,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,GAAG;IACZ,IAAI,EAAE,GAAG;CACV,CAAC;AAEF,MAAM,UAAU,OAAO,CAAC,EACtB,IAAI,GAAG,MAAM,EACb,KAAK,EACL,QAAQ,GAKT;IACC,OAAO,CACL,iBAAO,SAAS,EAAE,+BAA+B,IAAI,EAAE,aACrD,eAAM,SAAS,EAAC,oBAAoB,iCACjC,KAAK,CAAC,IAAI,CAAC,GACP,EACP,eAAK,SAAS,EAAC,oBAAoB,aAChC,KAAK,IAAI,YAAG,SAAS,EAAC,qBAAqB,YAAE,KAAK,GAAK,EACvD,QAAQ,IACL,IACA,CACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ export type ChartType = "bar" | "line" | "area" | "pie";
2
+ export interface ChartProps {
3
+ type: ChartType;
4
+ /** Inline data rows, e.g. [{ month: "Jan", clicks: 120 }, ...] */
5
+ data: Record<string, string | number>[];
6
+ /** Key used for the x axis (or pie labels). */
7
+ x: string;
8
+ /** Key (or keys) plotted as series (or pie values). */
9
+ y: string | string[];
10
+ height?: number;
11
+ /** Series colors; defaults derive from the --zanin-accent CSS variable. */
12
+ colors?: string[];
13
+ title?: string;
14
+ }
15
+ export declare function Chart({ type, data, x, y, height, colors, title }: ChartProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=Chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Chart.d.ts","sourceRoot":"","sources":["../../src/components/Chart.tsx"],"names":[],"mappings":"AAoBA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAExD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACxC,+CAA+C;IAC/C,CAAC,EAAE,MAAM,CAAC;IACV,uDAAuD;IACvD,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAWD,wBAAgB,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,MAAY,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,+BAwElF"}
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, Legend, Line, LineChart, Pie, PieChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts";
4
+ const DEFAULT_COLORS = [
5
+ "var(--zanin-accent, #2563eb)",
6
+ "#0ea5e9",
7
+ "#8b5cf6",
8
+ "#f59e0b",
9
+ "#10b981",
10
+ "#ef4444",
11
+ ];
12
+ export function Chart({ type, data, x, y, height = 320, colors, title }) {
13
+ const series = Array.isArray(y) ? y : [y];
14
+ const palette = colors ?? DEFAULT_COLORS;
15
+ let chart;
16
+ if (type === "pie") {
17
+ chart = (_jsxs(PieChart, { children: [_jsx(Tooltip, {}), _jsx(Pie, { data: data, dataKey: series[0], nameKey: x, outerRadius: "80%", label: true, children: data.map((_, i) => (_jsx(Cell, { fill: palette[i % palette.length] }, i))) })] }));
18
+ }
19
+ else if (type === "line" || type === "area") {
20
+ const Wrapper = type === "line" ? LineChart : AreaChart;
21
+ chart = (_jsxs(Wrapper, { data: data, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "var(--zanin-hairline, #e5e7eb)" }), _jsx(XAxis, { dataKey: x, fontSize: 12 }), _jsx(YAxis, { fontSize: 12, width: 40 }), _jsx(Tooltip, {}), series.length > 1 && _jsx(Legend, {}), series.map((key, i) => type === "line" ? (_jsx(Line, { type: "monotone", dataKey: key, stroke: palette[i % palette.length], strokeWidth: 2.5, dot: false }, key)) : (_jsx(Area, { type: "monotone", dataKey: key, stroke: palette[i % palette.length], fill: palette[i % palette.length], fillOpacity: 0.14, strokeWidth: 2.5 }, key)))] }));
22
+ }
23
+ else {
24
+ chart = (_jsxs(BarChart, { data: data, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "var(--zanin-hairline, #e5e7eb)" }), _jsx(XAxis, { dataKey: x, fontSize: 12 }), _jsx(YAxis, { fontSize: 12, width: 40 }), _jsx(Tooltip, {}), series.length > 1 && _jsx(Legend, {}), series.map((key, i) => (_jsx(Bar, { dataKey: key, fill: palette[i % palette.length], radius: [6, 6, 0, 0] }, key)))] }));
25
+ }
26
+ return (_jsxs("div", { className: "zanin-chart", children: [title && _jsx("p", { className: "zanin-chart-title", children: title }), _jsx(ResponsiveContainer, { width: "100%", height: height, children: chart })] }));
27
+ }
28
+ //# sourceMappingURL=Chart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Chart.js","sourceRoot":"","sources":["../../src/components/Chart.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EACL,IAAI,EACJ,SAAS,EACT,GAAG,EACH,QAAQ,EACR,aAAa,EACb,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,SAAS,EACT,GAAG,EACH,QAAQ,EACR,mBAAmB,EACnB,OAAO,EACP,KAAK,EACL,KAAK,GACN,MAAM,UAAU,CAAC;AAkBlB,MAAM,cAAc,GAAG;IACrB,8BAA8B;IAC9B,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;CACV,CAAC;AAEF,MAAM,UAAU,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE,MAAM,EAAE,KAAK,EAAc;IACjF,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,cAAc,CAAC;IAEzC,IAAI,KAAyB,CAAC;IAC9B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,KAAK,GAAG,CACN,MAAC,QAAQ,eACP,KAAC,OAAO,KAAG,EACX,KAAC,GAAG,IAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAC,KAAK,EAAC,KAAK,kBACrE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAClB,KAAC,IAAI,IAAS,IAAI,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,IAApC,CAAC,CAAuC,CACpD,CAAC,GACE,IACG,CACZ,CAAC;IACJ,CAAC;SAAM,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QACxD,KAAK,GAAG,CACN,MAAC,OAAO,IAAC,IAAI,EAAE,IAAI,aACjB,KAAC,aAAa,IAAC,eAAe,EAAC,KAAK,EAAC,MAAM,EAAC,gCAAgC,GAAG,EAC/E,KAAC,KAAK,IAAC,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,GAAI,EACnC,KAAC,KAAK,IAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,GAAI,EAClC,KAAC,OAAO,KAAG,EACV,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,KAAC,MAAM,KAAG,EAC/B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CACrB,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAChB,KAAC,IAAI,IAEH,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,GAAG,EACZ,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EACnC,WAAW,EAAE,GAAG,EAChB,GAAG,EAAE,KAAK,IALL,GAAG,CAMR,CACH,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAEH,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,GAAG,EACZ,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EACnC,IAAI,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EACjC,WAAW,EAAE,IAAI,EACjB,WAAW,EAAE,GAAG,IANX,GAAG,CAOR,CACH,CACF,IACO,CACX,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,CACN,MAAC,QAAQ,IAAC,IAAI,EAAE,IAAI,aAClB,KAAC,aAAa,IAAC,eAAe,EAAC,KAAK,EAAC,MAAM,EAAC,gCAAgC,GAAG,EAC/E,KAAC,KAAK,IAAC,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,GAAI,EACnC,KAAC,KAAK,IAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,GAAI,EAClC,KAAC,OAAO,KAAG,EACV,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,KAAC,MAAM,KAAG,EAC/B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CACtB,KAAC,GAAG,IAAW,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAA1E,GAAG,CAA2E,CACzF,CAAC,IACO,CACZ,CAAC;IACJ,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,aAAa,aACzB,KAAK,IAAI,YAAG,SAAS,EAAC,mBAAmB,YAAE,KAAK,GAAK,EACtD,KAAC,mBAAmB,IAAC,KAAK,EAAC,MAAM,EAAC,MAAM,EAAE,MAAM,YAC7C,KAAK,GACc,IAClB,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * Frame for fenced code blocks. MDX renders ```lang fences as
4
+ * <pre><code className="language-lang">, which the component map routes here.
5
+ */
6
+ export declare function CodeBlock({ children }: {
7
+ children?: ReactNode;
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=CodeBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../src/components/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+BAa/D"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Frame for fenced code blocks. MDX renders ```lang fences as
4
+ * <pre><code className="language-lang">, which the component map routes here.
5
+ */
6
+ export function CodeBlock({ children }) {
7
+ const code = children && typeof children === "object" && "props" in children
8
+ ? children
9
+ : null;
10
+ const language = code?.props.className?.replace("language-", "");
11
+ return (_jsxs("div", { className: "zanin-codeblock", children: [language && _jsx("span", { className: "zanin-codeblock-lang", children: language }), _jsx("pre", { children: children })] }));
12
+ }
13
+ //# sourceMappingURL=CodeBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeBlock.js","sourceRoot":"","sources":["../../src/components/CodeBlock.tsx"],"names":[],"mappings":";AAEA;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,QAAQ,EAA4B;IAC9D,MAAM,IAAI,GACR,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,IAAK,QAAmB;QACzE,CAAC,CAAE,QAAoE;QACvE,CAAC,CAAC,IAAI,CAAC;IACX,MAAM,QAAQ,GAAG,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAEjE,OAAO,CACL,eAAK,SAAS,EAAC,iBAAiB,aAC7B,QAAQ,IAAI,eAAM,SAAS,EAAC,sBAAsB,YAAE,QAAQ,GAAQ,EACrE,wBAAM,QAAQ,GAAO,IACjB,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare function ComparisonTable({ columns, rows, highlight, }: {
2
+ /** Header cells; the first is usually the feature/criteria column. */
3
+ columns: string[];
4
+ /** Row cells; use true/false for check/cross marks. */
5
+ rows: (string | number | boolean)[][];
6
+ /** Index of the column to visually emphasize (e.g. your product). */
7
+ highlight?: number;
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=ComparisonTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComparisonTable.d.ts","sourceRoot":"","sources":["../../src/components/ComparisonTable.tsx"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,IAAI,EACJ,SAAS,GACV,EAAE;IACD,sEAAsE;IACtE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uDAAuD;IACvD,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,EAAE,CAAC;IACtC,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAiCA"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function ComparisonTable({ columns, rows, highlight, }) {
3
+ const renderCell = (value) => {
4
+ if (value === true)
5
+ return _jsx("span", { className: "zanin-check", children: "\u2713" });
6
+ if (value === false)
7
+ return _jsx("span", { className: "zanin-cross", children: "\u2715" });
8
+ return value;
9
+ };
10
+ return (_jsx("div", { className: "zanin-comparison", children: _jsxs("table", { children: [_jsx("thead", { children: _jsx("tr", { children: columns.map((col, i) => (_jsx("th", { className: i === highlight ? "zanin-highlight" : undefined, children: col }, i))) }) }), _jsx("tbody", { children: rows.map((row, r) => (_jsx("tr", { children: row.map((cell, c) => (_jsx("td", { className: c === highlight ? "zanin-highlight" : undefined, children: renderCell(cell) }, c))) }, r))) })] }) }));
11
+ }
12
+ //# sourceMappingURL=ComparisonTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComparisonTable.js","sourceRoot":"","sources":["../../src/components/ComparisonTable.tsx"],"names":[],"mappings":";AAAA,MAAM,UAAU,eAAe,CAAC,EAC9B,OAAO,EACP,IAAI,EACJ,SAAS,GAQV;IACC,MAAM,UAAU,GAAG,CAAC,KAAgC,EAAE,EAAE;QACtD,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,eAAM,SAAS,EAAC,aAAa,uBAAS,CAAC;QAClE,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,eAAM,SAAS,EAAC,aAAa,uBAAS,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,OAAO,CACL,cAAK,SAAS,EAAC,kBAAkB,YAC/B,4BACE,0BACE,uBACG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CACvB,aAAY,SAAS,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,YACnE,GAAG,IADG,CAAC,CAEL,CACN,CAAC,GACC,GACC,EACR,0BACG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CACpB,uBACG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CACpB,aAAY,SAAS,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,YACnE,UAAU,CAAC,IAAI,CAAC,IADV,CAAC,CAEL,CACN,CAAC,IALK,CAAC,CAML,CACN,CAAC,GACI,IACF,GACJ,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare function YouTube({ id, title }: {
2
+ id: string;
3
+ title?: string;
4
+ }): import("react").JSX.Element;
5
+ /**
6
+ * Privacy-friendly tweet embed: renders the quoted text server-side and links
7
+ * out, instead of loading Twitter's widget script.
8
+ */
9
+ export declare function Tweet({ id, author, handle, children, }: {
10
+ id: string;
11
+ author?: string;
12
+ handle?: string;
13
+ children?: React.ReactNode;
14
+ }): import("react").JSX.Element;
15
+ //# sourceMappingURL=Embeds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Embeds.d.ts","sourceRoot":"","sources":["../../src/components/Embeds.tsx"],"names":[],"mappings":"AAAA,wBAAgB,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAYpE;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,EACpB,EAAE,EACF,MAAM,EACN,MAAM,EACN,QAAQ,GACT,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,+BAcA"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function YouTube({ id, title }) {
3
+ return (_jsx("div", { className: "zanin-embed zanin-embed-youtube", children: _jsx("iframe", { src: `https://www.youtube-nocookie.com/embed/${encodeURIComponent(id)}`, title: title ?? "YouTube video", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture", allowFullScreen: true, loading: "lazy" }) }));
4
+ }
5
+ /**
6
+ * Privacy-friendly tweet embed: renders the quoted text server-side and links
7
+ * out, instead of loading Twitter's widget script.
8
+ */
9
+ export function Tweet({ id, author, handle, children, }) {
10
+ const url = `https://x.com/${handle ?? "i"}/status/${encodeURIComponent(id)}`;
11
+ return (_jsxs("blockquote", { className: "zanin-tweet", children: [children && _jsx("div", { className: "zanin-tweet-text", children: children }), _jsxs("footer", { children: [author && _jsx("span", { className: "zanin-tweet-author", children: author }), handle && _jsxs("span", { className: "zanin-tweet-handle", children: ["@", handle] }), _jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", children: "View on X" })] })] }));
12
+ }
13
+ //# sourceMappingURL=Embeds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Embeds.js","sourceRoot":"","sources":["../../src/components/Embeds.tsx"],"names":[],"mappings":";AAAA,MAAM,UAAU,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAkC;IACnE,OAAO,CACL,cAAK,SAAS,EAAC,iCAAiC,YAC9C,iBACE,GAAG,EAAE,0CAA0C,kBAAkB,CAAC,EAAE,CAAC,EAAE,EACvE,KAAK,EAAE,KAAK,IAAI,eAAe,EAC/B,KAAK,EAAC,0FAA0F,EAChG,eAAe,QACf,OAAO,EAAC,MAAM,GACd,GACE,CACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,EACpB,EAAE,EACF,MAAM,EACN,MAAM,EACN,QAAQ,GAMT;IACC,MAAM,GAAG,GAAG,iBAAiB,MAAM,IAAI,GAAG,WAAW,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC;IAC9E,OAAO,CACL,sBAAY,SAAS,EAAC,aAAa,aAChC,QAAQ,IAAI,cAAK,SAAS,EAAC,kBAAkB,YAAE,QAAQ,GAAO,EAC/D,6BACG,MAAM,IAAI,eAAM,SAAS,EAAC,oBAAoB,YAAE,MAAM,GAAQ,EAC9D,MAAM,IAAI,gBAAM,SAAS,EAAC,oBAAoB,kBAAG,MAAM,IAAQ,EAChE,YAAG,IAAI,EAAE,GAAG,EAAE,MAAM,EAAC,QAAQ,EAAC,GAAG,EAAC,qBAAqB,0BAEnD,IACG,IACE,CACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface FaqItem {
2
+ q: string;
3
+ a: string;
4
+ }
5
+ /** FAQ accordion with schema.org FAQPage markup for rich results. */
6
+ export declare function FAQ({ items }: {
7
+ items: FaqItem[];
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=FAQ.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FAQ.d.ts","sourceRoot":"","sources":["../../src/components/FAQ.tsx"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,qEAAqE;AACrE,wBAAgB,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,OAAO,EAAE,CAAA;CAAE,+BAyBlD"}
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** FAQ accordion with schema.org FAQPage markup for rich results. */
3
+ export function FAQ({ items }) {
4
+ const jsonLd = {
5
+ "@context": "https://schema.org",
6
+ "@type": "FAQPage",
7
+ mainEntity: items.map((item) => ({
8
+ "@type": "Question",
9
+ name: item.q,
10
+ acceptedAnswer: { "@type": "Answer", text: item.a },
11
+ })),
12
+ };
13
+ return (_jsxs("section", { className: "zanin-faq", children: [_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(jsonLd) } }), items.map((item, i) => (_jsxs("details", { children: [_jsx("summary", { children: item.q }), _jsx("p", { children: item.a })] }, i)))] }));
14
+ }
15
+ //# sourceMappingURL=FAQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FAQ.js","sourceRoot":"","sources":["../../src/components/FAQ.tsx"],"names":[],"mappings":";AAKA,qEAAqE;AACrE,MAAM,UAAU,GAAG,CAAC,EAAE,KAAK,EAAwB;IACjD,MAAM,MAAM,GAAG;QACb,UAAU,EAAE,oBAAoB;QAChC,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/B,OAAO,EAAE,UAAU;YACnB,IAAI,EAAE,IAAI,CAAC,CAAC;YACZ,cAAc,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE;SACpD,CAAC,CAAC;KACJ,CAAC;IAEF,OAAO,CACL,mBAAS,SAAS,EAAC,WAAW,aAC5B,iBACE,IAAI,EAAC,qBAAqB,EAC1B,uBAAuB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,GAC3D,EACD,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CACtB,8BACE,4BAAU,IAAI,CAAC,CAAC,GAAW,EAC3B,sBAAI,IAAI,CAAC,CAAC,GAAK,KAFH,CAAC,CAGL,CACX,CAAC,IACM,CACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare function Figure({ src, alt, caption, width, height, }: {
2
+ src: string;
3
+ alt: string;
4
+ caption?: string;
5
+ width?: number;
6
+ height?: number;
7
+ }): import("react").JSX.Element;
8
+ //# sourceMappingURL=Figure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Figure.d.ts","sourceRoot":"","sources":["../../src/components/Figure.tsx"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,EACrB,GAAG,EACH,GAAG,EACH,OAAO,EACP,KAAK,EACL,MAAM,GACP,EAAE;IACD,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,+BASA"}
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function Figure({ src, alt, caption, width, height, }) {
3
+ return (_jsxs("figure", { className: "zanin-figure", children: [_jsx("img", { src: src, alt: alt, width: width, height: height, loading: "lazy" }), caption && _jsx("figcaption", { children: caption })] }));
4
+ }
5
+ //# sourceMappingURL=Figure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Figure.js","sourceRoot":"","sources":["../../src/components/Figure.tsx"],"names":[],"mappings":";AAAA,MAAM,UAAU,MAAM,CAAC,EACrB,GAAG,EACH,GAAG,EACH,OAAO,EACP,KAAK,EACL,MAAM,GAOP;IACC,OAAO,CACL,kBAAQ,SAAS,EAAC,cAAc,aAG9B,cAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAC,MAAM,GAAG,EACvE,OAAO,IAAI,+BAAa,OAAO,GAAc,IACvC,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,30 @@
1
+ export interface ZaninBlogConfig {
2
+ /** Base URL of the Zanin CMS deployment. Falls back to ZANIN_BASE_URL. */
3
+ baseUrl?: string;
4
+ /** Site id from the Zanin dashboard. Falls back to ZANIN_SITE_ID. */
5
+ siteId?: string;
6
+ /** Workspace API key ("zn_..."). Falls back to ZANIN_SITE_KEY / ZANIN_API_KEY. */
7
+ apiKey?: string;
8
+ /** Path the blog is mounted at in the host site. Default "/blog". */
9
+ basePath?: string;
10
+ /** Public origin of the host site, e.g. "https://ongod.app". Used for canonical URLs, RSS and sitemap. Falls back to ZANIN_SITE_URL. */
11
+ siteUrl?: string;
12
+ /** Host site name, used in metadata titles and the RSS channel. */
13
+ siteName?: string;
14
+ /** ISR revalidation window in seconds for content fetches. Default 300. */
15
+ revalidate?: number | false;
16
+ /** Secret expected on the revalidation webhook (x-zanin-secret header). Falls back to ZANIN_REVALIDATE_SECRET. */
17
+ revalidateSecret?: string;
18
+ }
19
+ export interface ResolvedZaninBlogConfig {
20
+ baseUrl: string;
21
+ siteId: string;
22
+ apiKey: string;
23
+ basePath: string;
24
+ siteUrl?: string;
25
+ siteName?: string;
26
+ revalidate: number | false;
27
+ revalidateSecret?: string;
28
+ }
29
+ export declare function resolveConfig(config?: ZaninBlogConfig): ResolvedZaninBlogConfig;
30
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wIAAwI;IACxI,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,kHAAkH;IAClH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,aAAa,CAAC,MAAM,GAAE,eAAoB,GAAG,uBAAuB,CAsBnF"}
package/dist/config.js ADDED
@@ -0,0 +1,27 @@
1
+ export function resolveConfig(config = {}) {
2
+ const baseUrl = config.baseUrl ?? process.env.ZANIN_BASE_URL;
3
+ const siteId = config.siteId ?? process.env.ZANIN_SITE_ID;
4
+ const apiKey = config.apiKey ?? process.env.ZANIN_SITE_KEY ?? process.env.ZANIN_API_KEY;
5
+ if (!baseUrl)
6
+ throw new Error("@usezanin/blog: baseUrl (or ZANIN_BASE_URL) is required");
7
+ if (!siteId)
8
+ throw new Error("@usezanin/blog: siteId (or ZANIN_SITE_ID) is required");
9
+ if (!apiKey) {
10
+ throw new Error("@usezanin/blog: apiKey (or ZANIN_SITE_KEY) is required");
11
+ }
12
+ return {
13
+ baseUrl: baseUrl.replace(/\/+$/, ""),
14
+ siteId,
15
+ apiKey,
16
+ basePath: normalizeBasePath(config.basePath ?? "/blog"),
17
+ siteUrl: (config.siteUrl ?? process.env.ZANIN_SITE_URL)?.replace(/\/+$/, ""),
18
+ siteName: config.siteName,
19
+ revalidate: config.revalidate ?? 300,
20
+ revalidateSecret: config.revalidateSecret ?? process.env.ZANIN_REVALIDATE_SECRET,
21
+ };
22
+ }
23
+ function normalizeBasePath(path) {
24
+ const withLeading = path.startsWith("/") ? path : `/${path}`;
25
+ return withLeading.replace(/\/+$/, "") || "/";
26
+ }
27
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA8BA,MAAM,UAAU,aAAa,CAAC,SAA0B,EAAE;IACxD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC7D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAC1D,MAAM,MAAM,GACV,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAE3E,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACzF,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACtF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,MAAM;QACN,MAAM;QACN,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC;QACvD,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5E,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,GAAG;QACpC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB;KACjF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAC7D,OAAO,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;AAChD,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { MDXComponents } from "mdx/types";
2
+ import type { ResolvedZaninBlogConfig } from "./config.js";
3
+ /**
4
+ * Fetches the site's custom components and evaluates their compiled modules
5
+ * into React components, keyed by MDX tag name. Failures are skipped (with a
6
+ * server-side log) rather than breaking the whole post.
7
+ */
8
+ export declare function fetchCustomComponents(config: ResolvedZaninBlogConfig): Promise<MDXComponents>;
9
+ //# sourceMappingURL=custom-components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-components.d.ts","sourceRoot":"","sources":["../src/custom-components.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAuC3D;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,aAAa,CAAC,CAkBxB"}