@tekki-forge/runtime-admin 0.2.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/dist/admin-dashboard.d.ts +16 -0
- package/dist/admin-dashboard.js +60 -0
- package/dist/admin-dashboard.js.map +1 -0
- package/dist/admin-orgs.d.ts +14 -0
- package/dist/admin-orgs.js +75 -0
- package/dist/admin-orgs.js.map +1 -0
- package/dist/admin-shell.d.ts +55 -0
- package/dist/admin-shell.js +141 -0
- package/dist/admin-shell.js.map +1 -0
- package/dist/admin.js +35 -4
- package/dist/admin.js.map +1 -1
- package/dist/app-pages.d.ts +19 -0
- package/dist/app-pages.js +109 -0
- package/dist/app-pages.js.map +1 -0
- package/dist/auth-pages.js +7 -2
- package/dist/auth-pages.js.map +1 -1
- package/dist/charts.d.ts +98 -2
- package/dist/charts.js +264 -24
- package/dist/charts.js.map +1 -1
- package/dist/context.js +32 -1
- package/dist/context.js.map +1 -1
- package/dist/dashboard.d.ts +63 -8
- package/dist/dashboard.js +180 -36
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/messages/de.js +751 -0
- package/dist/i18n/messages/de.js.map +1 -1
- package/dist/i18n/messages/en.js +751 -0
- package/dist/i18n/messages/en.js.map +1 -1
- package/dist/i18n/messages/es.js +751 -0
- package/dist/i18n/messages/es.js.map +1 -1
- package/dist/i18n/messages/fr.js +751 -0
- package/dist/i18n/messages/fr.js.map +1 -1
- package/dist/i18n/messages/pt.js +751 -0
- package/dist/i18n/messages/pt.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/shell.js +37 -34
- package/dist/shell.js.map +1 -1
- package/dist/site/editor/api.d.ts +74 -0
- package/dist/site/editor/api.js +93 -0
- package/dist/site/editor/api.js.map +1 -0
- package/dist/site/editor/content-page.d.ts +4 -0
- package/dist/site/editor/content-page.js +65 -0
- package/dist/site/editor/content-page.js.map +1 -0
- package/dist/site/editor/inbox-tab.d.ts +4 -0
- package/dist/site/editor/inbox-tab.js +88 -0
- package/dist/site/editor/inbox-tab.js.map +1 -0
- package/dist/site/editor/index.d.ts +1 -0
- package/dist/site/editor/index.js +3 -0
- package/dist/site/editor/index.js.map +1 -0
- package/dist/site/editor/model.d.ts +145 -0
- package/dist/site/editor/model.js +408 -0
- package/dist/site/editor/model.js.map +1 -0
- package/dist/site/editor/page-editor.d.ts +10 -0
- package/dist/site/editor/page-editor.js +257 -0
- package/dist/site/editor/page-editor.js.map +1 -0
- package/dist/site/editor/page-settings.d.ts +9 -0
- package/dist/site/editor/page-settings.js +105 -0
- package/dist/site/editor/page-settings.js.map +1 -0
- package/dist/site/editor/pages-tab.d.ts +5 -0
- package/dist/site/editor/pages-tab.js +108 -0
- package/dist/site/editor/pages-tab.js.map +1 -0
- package/dist/site/editor/posts-tab.d.ts +6 -0
- package/dist/site/editor/posts-tab.js +139 -0
- package/dist/site/editor/posts-tab.js.map +1 -0
- package/dist/site/editor/preview.d.ts +32 -0
- package/dist/site/editor/preview.js +104 -0
- package/dist/site/editor/preview.js.map +1 -0
- package/dist/site/editor/prop-fields.d.ts +53 -0
- package/dist/site/editor/prop-fields.js +233 -0
- package/dist/site/editor/prop-fields.js.map +1 -0
- package/dist/site/editor/section-list.d.ts +14 -0
- package/dist/site/editor/section-list.js +110 -0
- package/dist/site/editor/section-list.js.map +1 -0
- package/dist/site/editor/section-picker.d.ts +12 -0
- package/dist/site/editor/section-picker.js +44 -0
- package/dist/site/editor/section-picker.js.map +1 -0
- package/dist/site/editor/widgets.d.ts +84 -0
- package/dist/site/editor/widgets.js +130 -0
- package/dist/site/editor/widgets.js.map +1 -0
- package/dist/site/format.d.ts +31 -0
- package/dist/site/format.js +106 -0
- package/dist/site/format.js.map +1 -0
- package/dist/site/forms.d.ts +43 -0
- package/dist/site/forms.js +155 -0
- package/dist/site/forms.js.map +1 -0
- package/dist/site/header.d.ts +32 -0
- package/dist/site/header.js +76 -0
- package/dist/site/header.js.map +1 -0
- package/dist/site/icons.d.ts +10 -0
- package/dist/site/icons.js +28 -0
- package/dist/site/icons.js.map +1 -0
- package/dist/site/index.d.ts +11 -0
- package/dist/site/index.js +13 -0
- package/dist/site/index.js.map +1 -0
- package/dist/site/markdown.d.ts +14 -0
- package/dist/site/markdown.js +457 -0
- package/dist/site/markdown.js.map +1 -0
- package/dist/site/page.d.ts +16 -0
- package/dist/site/page.js +65 -0
- package/dist/site/page.js.map +1 -0
- package/dist/site/proxy.d.ts +13 -0
- package/dist/site/proxy.js +43 -0
- package/dist/site/proxy.js.map +1 -0
- package/dist/site/sections.d.ts +43 -0
- package/dist/site/sections.js +560 -0
- package/dist/site/sections.js.map +1 -0
- package/dist/site/server.d.ts +73 -0
- package/dist/site/server.js +235 -0
- package/dist/site/server.js.map +1 -0
- package/dist/site/types.d.ts +137 -0
- package/dist/site/types.js +2 -0
- package/dist/site/types.js.map +1 -0
- package/dist/support.d.ts +29 -0
- package/dist/support.js +165 -0
- package/dist/support.js.map +1 -0
- package/dist/system-config.d.ts +24 -0
- package/dist/system-config.js +37 -0
- package/dist/system-config.js.map +1 -0
- package/dist/system-settings.d.ts +9 -0
- package/dist/system-settings.js +60 -0
- package/dist/system-settings.js.map +1 -0
- package/dist/theme-catalog.d.ts +72 -2
- package/dist/theme-catalog.js +47 -0
- package/dist/theme-catalog.js.map +1 -1
- package/dist/theme-switcher.d.ts +8 -0
- package/dist/theme-switcher.js +39 -4
- package/dist/theme-switcher.js.map +1 -1
- package/dist/theme.js +8 -0
- package/dist/theme.js.map +1 -1
- package/dist/transactions.d.ts +4 -0
- package/dist/transactions.js +60 -0
- package/dist/transactions.js.map +1 -0
- package/package.json +9 -5
- package/styles.css +1089 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { embedAllowed, parseVideo, validHref, validImage } from "@tekki-forge/compiler/site";
|
|
3
|
+
import { ArrowRight, ArrowUpRight, Check, EyeOff, Mail, MapPin, Minus, Phone, Plus, Quote, Rss } from "lucide-react";
|
|
4
|
+
import { BrandPattern, brandMeshStyle } from "../brand-art.js";
|
|
5
|
+
import { ContactForm, NewsletterForm } from "./forms.js";
|
|
6
|
+
import { entityField, SiteFieldValue, fileOf, formatDate, formatFieldText, rowValue, siteTranslator } from "./format.js";
|
|
7
|
+
import { SiteIcon } from "./icons.js";
|
|
8
|
+
import { Markdown } from "./markdown.js";
|
|
9
|
+
const isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
10
|
+
const str = (v) => (typeof v === "string" && v.trim() ? v : undefined);
|
|
11
|
+
const items = (v) => (Array.isArray(v) ? v.filter(isObj) : []);
|
|
12
|
+
const strings = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === "string" && !!x.trim()) : []);
|
|
13
|
+
const href = (v) => (validHref(v) ? v : undefined);
|
|
14
|
+
const image = (v) => (validImage(v) ? v : undefined);
|
|
15
|
+
const external = (h) => /^https?:\/\//i.test(h);
|
|
16
|
+
const rel = (h) => (external(h) ? { rel: "noopener" } : {});
|
|
17
|
+
const link = (v) => (isObj(v) && str(v.label) && validHref(v.href) ? { label: String(v.label), href: v.href } : undefined);
|
|
18
|
+
const safeId = (key) => key.replace(/[^\w-]/g, "-");
|
|
19
|
+
const initials = (name) => name
|
|
20
|
+
.split(/\s+/)
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.slice(0, 2)
|
|
23
|
+
.map((w) => w[0].toUpperCase())
|
|
24
|
+
.join("");
|
|
25
|
+
/** Literal class names per column count (Tailwind only sees complete class names). */
|
|
26
|
+
const COLS_3XL = { 1: "@3xl:grid-cols-1", 2: "@3xl:grid-cols-2", 3: "@3xl:grid-cols-3", 4: "@3xl:grid-cols-4", 5: "@3xl:grid-cols-5" };
|
|
27
|
+
const cols = (n, max) => COLS_3XL[Math.max(1, Math.min(n, max))];
|
|
28
|
+
function Section({ ctx, children, width = "max-w-6xl", pad = "py-16 @3xl:py-24", className = "", labelled = true }) {
|
|
29
|
+
const id = safeId(ctx.section.key);
|
|
30
|
+
return (_jsx("section", { id: id, "aria-labelledby": labelled ? `${id}-title` : undefined, "data-section": ctx.section.type, "data-variant": ctx.section.variant, className: `forge-site-section px-4 @3xl:px-8 ${pad} ${className}`.trim(), children: _jsx("div", { className: `mx-auto ${width}`, children: children }) }));
|
|
31
|
+
}
|
|
32
|
+
/** Eyebrow, title (the section's h2) and subtitle; nothing when all three are empty. */
|
|
33
|
+
function SectionHeading({ ctx, center = false, className = "mb-10 @3xl:mb-14", children }) {
|
|
34
|
+
const p = ctx.section.props;
|
|
35
|
+
const eyebrow = str(p.eyebrow);
|
|
36
|
+
const title = str(p.title);
|
|
37
|
+
const subtitle = str(p.subtitle);
|
|
38
|
+
if (!eyebrow && !title && !subtitle && !children)
|
|
39
|
+
return null;
|
|
40
|
+
return (_jsxs("div", { className: `forge-site-heading max-w-3xl ${center ? "mx-auto text-center" : ""} ${className}`.trim(), children: [eyebrow && _jsx("p", { className: "forge-site-eyebrow text-sm font-semibold", children: eyebrow }), title && (_jsx("h2", { id: `${safeId(ctx.section.key)}-title`, className: `forge-heading forge-site-title text-3xl leading-tight font-semibold tracking-tight text-balance text-ink @3xl:text-4xl ${eyebrow ? "mt-3" : ""}`, children: title })), subtitle && _jsx("p", { className: "mt-4 text-lg leading-relaxed text-pretty whitespace-pre-line text-ink-muted", children: subtitle }), children] }));
|
|
41
|
+
}
|
|
42
|
+
/** Item headings: h3 under a section title, h2 when the section has none (no skipped levels). */
|
|
43
|
+
const itemTag = (ctx) => (str(ctx.section.props.title) ? "h3" : "h2");
|
|
44
|
+
const hasTitle = (ctx) => !!str(ctx.section.props.title);
|
|
45
|
+
const BUTTON = {
|
|
46
|
+
primary: "forge-btn-primary",
|
|
47
|
+
secondary: "forge-btn-secondary",
|
|
48
|
+
onBrand: "forge-landing-cta",
|
|
49
|
+
onBrandSecondary: "forge-landing-cta-secondary",
|
|
50
|
+
};
|
|
51
|
+
function Button({ to, tone, arrow = false }) {
|
|
52
|
+
return (_jsxs("a", { href: to.href, ...rel(to.href), className: `forge-btn ${BUTTON[tone]} forge-site-btn inline-flex h-11 items-center justify-center gap-1.5 px-5 text-base font-medium`, children: [to.label, arrow && _jsx(ArrowRight, { className: "forge-site-arrow size-4", "aria-hidden": true })] }));
|
|
53
|
+
}
|
|
54
|
+
function Buttons({ primary, secondary, onBrand = false, center = false, className = "mt-10" }) {
|
|
55
|
+
if (!primary && !secondary)
|
|
56
|
+
return null;
|
|
57
|
+
return (_jsxs("div", { className: `flex flex-wrap gap-3 ${center ? "justify-center" : ""} ${className}`.trim(), children: [primary && _jsx(Button, { to: primary, tone: onBrand ? "onBrand" : "primary", arrow: true }), secondary && _jsx(Button, { to: secondary, tone: onBrand ? "onBrandSecondary" : "secondary" })] }));
|
|
58
|
+
}
|
|
59
|
+
/** An image in a framed box of fixed aspect ratio (no layout shift while it loads). */
|
|
60
|
+
function Media({ src, alt, ratio = "aspect-[4/3]", className = "", eager = false }) {
|
|
61
|
+
return (_jsx("div", { className: `forge-site-media overflow-hidden ${ratio} ${className}`.trim(), children: _jsx("img", { src: src, alt: alt, loading: eager ? "eager" : "lazy", decoding: "async", ...(eager ? { fetchPriority: "high" } : {}), className: "size-full object-cover" }) }));
|
|
62
|
+
}
|
|
63
|
+
function Avatar({ src, name, className = "size-10" }) {
|
|
64
|
+
if (src) {
|
|
65
|
+
// eslint-disable-next-line @next/next/no-img-element -- any URL or public file
|
|
66
|
+
return _jsx("img", { src: src, alt: "", loading: "lazy", decoding: "async", className: `forge-site-avatar shrink-0 object-cover ${className}` });
|
|
67
|
+
}
|
|
68
|
+
return (_jsx("span", { "aria-hidden": true, className: `forge-avatar flex shrink-0 items-center justify-center text-sm font-semibold ${className}`, children: initials(name) }));
|
|
69
|
+
}
|
|
70
|
+
/** "Learn more" with the item's name for screen readers. */
|
|
71
|
+
function MoreLink({ to, label, name, className = "mt-5" }) {
|
|
72
|
+
return (_jsxs("a", { href: to, ...rel(to), className: `forge-focusable forge-site-more inline-flex items-center gap-1 rounded-sm text-sm font-medium text-link ${className}`, children: [label, _jsxs("span", { className: "sr-only", children: [": ", name] }), _jsx(ArrowRight, { className: "forge-site-arrow size-3.5", "aria-hidden": true })] }));
|
|
73
|
+
}
|
|
74
|
+
// ─── Hero ───────────────────────────────────────────────────────────────────
|
|
75
|
+
function Hero(ctx) {
|
|
76
|
+
const p = ctx.section.props;
|
|
77
|
+
const title = str(p.title);
|
|
78
|
+
if (!title)
|
|
79
|
+
return null;
|
|
80
|
+
const Tag = ctx.first ? "h1" : "h2";
|
|
81
|
+
const id = safeId(ctx.section.key);
|
|
82
|
+
const eyebrow = str(p.eyebrow);
|
|
83
|
+
const subtitle = str(p.subtitle);
|
|
84
|
+
const primary = link(p.primary);
|
|
85
|
+
const secondary = link(p.secondary);
|
|
86
|
+
const img = image(p.image);
|
|
87
|
+
const alt = str(p.imageAlt) ?? "";
|
|
88
|
+
const heading = (className) => (_jsx(Tag, { id: `${id}-title`, className: `forge-heading forge-site-display font-semibold tracking-tight text-balance ${className}`, children: title }));
|
|
89
|
+
const variant = ctx.section.variant;
|
|
90
|
+
if (variant === "brand") {
|
|
91
|
+
return (_jsx(Section, { ctx: ctx, pad: "pt-4 pb-10 @3xl:pt-6 @3xl:pb-16", className: "forge-site-hero", children: _jsxs("div", { className: "forge-auth-brand forge-landing-hero forge-site-brand relative overflow-hidden", style: brandMeshStyle(ctx.app.name), children: [_jsx(BrandPattern, { name: ctx.app.name, id: `forge-site-dots-${id}` }), _jsxs("div", { className: `relative grid items-center gap-10 px-6 py-16 @3xl:px-12 @3xl:py-24 ${img ? "@5xl:grid-cols-[minmax(0,1.15fr)_minmax(0,1fr)]" : ""}`, children: [_jsxs("div", { className: "forge-auth-copy max-w-2xl", children: [eyebrow && _jsx("p", { className: "forge-site-brand-pill mb-6 inline-flex items-center px-3 py-1 text-sm font-medium", children: eyebrow }), heading("text-4xl leading-[1.06] @3xl:text-5xl @5xl:text-6xl"), subtitle && _jsx("p", { className: "mt-6 max-w-xl text-lg leading-relaxed text-pretty whitespace-pre-line opacity-90 @3xl:text-xl", children: subtitle }), _jsx(Buttons, { primary: primary, secondary: secondary, onBrand: true })] }), img && (_jsx("div", { className: "forge-landing-window p-2", children: _jsx(Media, { src: img, alt: alt, ratio: "aspect-[4/3]", eager: ctx.first, className: "forge-site-media-inner" }) }))] })] }) }));
|
|
92
|
+
}
|
|
93
|
+
if (variant === "split") {
|
|
94
|
+
return (_jsx(Section, { ctx: ctx, pad: "py-16 @3xl:py-24", className: "forge-site-hero forge-site-hero-glow relative isolate overflow-hidden", children: _jsxs("div", { className: `grid items-center gap-12 ${img ? "@3xl:grid-cols-2 @5xl:gap-16" : ""}`, children: [_jsxs("div", { className: "max-w-2xl", children: [eyebrow && _jsx("p", { className: "forge-chip forge-site-pill mb-6 inline-flex px-3 py-1 text-sm font-medium", children: eyebrow }), heading("text-4xl leading-[1.06] text-ink @3xl:text-5xl @5xl:text-6xl"), subtitle && _jsx("p", { className: "mt-6 text-lg leading-relaxed text-pretty whitespace-pre-line text-ink-muted @3xl:text-xl", children: subtitle }), _jsx(Buttons, { primary: primary, secondary: secondary })] }), img && _jsx(Media, { src: img, alt: alt, ratio: "aspect-[4/3]", eager: ctx.first, className: "forge-site-frame" })] }) }));
|
|
95
|
+
}
|
|
96
|
+
if (variant === "minimal") {
|
|
97
|
+
return (_jsxs(Section, { ctx: ctx, pad: "pt-14 pb-10 @3xl:pt-20 @3xl:pb-14", className: "forge-site-hero", children: [_jsxs("div", { className: "max-w-3xl", children: [eyebrow && _jsx("p", { className: "forge-site-eyebrow mb-4 text-sm font-semibold", children: eyebrow }), heading("text-4xl leading-[1.08] text-ink @3xl:text-5xl"), subtitle && _jsx("p", { className: "mt-5 text-lg leading-relaxed text-pretty whitespace-pre-line text-ink-muted @3xl:text-xl", children: subtitle }), _jsx(Buttons, { primary: primary, secondary: secondary, className: "mt-8" })] }), img && _jsx(Media, { src: img, alt: alt, ratio: "aspect-[21/9]", eager: ctx.first, className: "forge-site-frame mt-12" })] }));
|
|
98
|
+
}
|
|
99
|
+
// centered
|
|
100
|
+
return (_jsxs(Section, { ctx: ctx, pad: "pt-16 pb-16 @3xl:pt-28 @3xl:pb-24", className: "forge-site-hero forge-site-hero-glow relative isolate overflow-hidden", children: [_jsxs("div", { className: "mx-auto max-w-3xl text-center", children: [eyebrow && _jsx("p", { className: "forge-chip forge-site-pill mb-6 inline-flex px-3 py-1 text-sm font-medium", children: eyebrow }), heading("text-4xl leading-[1.06] text-ink @3xl:text-6xl"), subtitle && _jsx("p", { className: "mx-auto mt-6 max-w-2xl text-lg leading-relaxed text-pretty whitespace-pre-line text-ink-muted @3xl:text-xl", children: subtitle }), _jsx(Buttons, { primary: primary, secondary: secondary, center: true })] }), img && _jsx(Media, { src: img, alt: alt, ratio: "aspect-[16/9]", eager: ctx.first, className: "forge-site-frame mx-auto mt-14 max-w-5xl @3xl:mt-20" })] }));
|
|
101
|
+
}
|
|
102
|
+
// ─── Features ───────────────────────────────────────────────────────────────
|
|
103
|
+
function Features(ctx) {
|
|
104
|
+
const list = items(ctx.section.props.items).filter((i) => str(i.title));
|
|
105
|
+
if (!list.length)
|
|
106
|
+
return null;
|
|
107
|
+
const H = itemTag(ctx);
|
|
108
|
+
const more = ctx.t("site.learnMore");
|
|
109
|
+
const variant = ctx.section.variant;
|
|
110
|
+
if (variant === "alternating") {
|
|
111
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx, center: true, className: "mb-14 @3xl:mb-20" }), _jsx("ul", { className: "grid gap-16 @3xl:gap-24", children: list.map((f, i) => {
|
|
112
|
+
const img = image(f.image);
|
|
113
|
+
const to = href(f.href);
|
|
114
|
+
return (_jsxs("li", { className: "grid items-center gap-8 @3xl:grid-cols-2 @3xl:gap-16", children: [img ? (_jsx(Media, { src: img, alt: str(f.imageAlt) ?? "", className: `forge-site-frame ${i % 2 ? "@3xl:order-last" : ""}` })) : (_jsx("div", { "aria-hidden": true, className: `forge-site-iconpanel flex aspect-[4/3] items-center justify-center ${i % 2 ? "@3xl:order-last" : ""}`, children: _jsx("span", { className: "forge-chip flex size-20 items-center justify-center", children: _jsx(SiteIcon, { name: f.icon, className: "size-9" }) }) })), _jsxs("div", { className: "max-w-xl", children: [img && str(f.icon) && (_jsx("span", { className: "forge-chip mb-5 flex size-11 items-center justify-center", children: _jsx(SiteIcon, { name: f.icon }) })), _jsx(H, { className: "forge-heading text-2xl font-semibold tracking-tight text-balance text-ink @3xl:text-3xl", children: String(f.title) }), str(f.description) && _jsx("p", { className: "mt-4 text-lg leading-relaxed text-pretty whitespace-pre-line text-ink-muted", children: String(f.description) }), to && _jsx(MoreLink, { to: to, label: more, name: String(f.title), className: "mt-6" })] })] }, i));
|
|
115
|
+
}) })] }));
|
|
116
|
+
}
|
|
117
|
+
if (variant === "bento") {
|
|
118
|
+
// A rhythm of one large tile, small tiles and a wide tile, repeating every seven items.
|
|
119
|
+
const span = (i) => (i % 7 === 0 ? "@3xl:col-span-2 @3xl:row-span-2" : i % 7 === 4 ? "@3xl:col-span-2" : "");
|
|
120
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: "grid grid-flow-dense gap-4 @md:grid-cols-2 @3xl:grid-cols-3", children: list.map((f, i) => {
|
|
121
|
+
const img = image(f.image);
|
|
122
|
+
const to = href(f.href);
|
|
123
|
+
const big = i % 7 === 0;
|
|
124
|
+
return (_jsxs("li", { className: `forge-card forge-site-card forge-site-bento flex flex-col overflow-hidden ${big ? "forge-site-bento-feature" : ""} ${span(i)}`, children: [_jsxs("div", { className: `flex flex-col ${big ? "p-7 @3xl:p-9" : "p-6"}`, children: [_jsx("span", { className: `forge-chip flex items-center justify-center ${big ? "size-12" : "size-10"}`, children: _jsx(SiteIcon, { name: f.icon, className: big ? "size-6" : "size-5" }) }), _jsx(H, { className: `forge-heading mt-5 font-semibold tracking-tight text-ink ${big ? "text-2xl @3xl:text-3xl" : "text-lg"}`, children: String(f.title) }), str(f.description) && _jsx("p", { className: `mt-2 leading-relaxed text-pretty whitespace-pre-line text-ink-muted ${big ? "text-base @3xl:text-lg" : "text-sm"}`, children: String(f.description) }), to && _jsx(MoreLink, { to: to, label: more, name: String(f.title) })] }), img && _jsx(Media, { src: img, alt: str(f.imageAlt) ?? "", ratio: big ? "aspect-[16/10]" : "aspect-[16/9]", className: "forge-site-bento-media mt-auto" })] }, i));
|
|
125
|
+
}) })] }));
|
|
126
|
+
}
|
|
127
|
+
// grid
|
|
128
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: "grid gap-5 @md:grid-cols-2 @5xl:grid-cols-3", children: list.map((f, i) => {
|
|
129
|
+
const img = image(f.image);
|
|
130
|
+
const to = href(f.href);
|
|
131
|
+
return (_jsxs("li", { className: `forge-card forge-site-card flex flex-col overflow-hidden ${to ? "forge-card-interactive" : ""}`, children: [img && _jsx(Media, { src: img, alt: str(f.imageAlt) ?? "", ratio: "aspect-[16/10]", className: "forge-site-card-media" }), _jsxs("div", { className: "flex flex-1 flex-col p-[var(--forge-pad-card-lg)]", children: [!img && (_jsx("span", { className: "forge-chip flex size-10 items-center justify-center", children: _jsx(SiteIcon, { name: f.icon }) })), _jsx(H, { className: `forge-heading text-lg font-semibold text-ink ${img ? "" : "mt-5"}`, children: String(f.title) }), str(f.description) && _jsx("p", { className: "mt-2 text-sm leading-relaxed text-pretty whitespace-pre-line text-ink-muted", children: String(f.description) }), to && _jsx(MoreLink, { to: to, label: more, name: String(f.title), className: "mt-auto pt-5" })] })] }, i));
|
|
132
|
+
}) })] }));
|
|
133
|
+
}
|
|
134
|
+
// ─── Logos ──────────────────────────────────────────────────────────────────
|
|
135
|
+
function LogoMark({ item, grid }) {
|
|
136
|
+
const name = String(item.name);
|
|
137
|
+
const img = image(item.image);
|
|
138
|
+
const to = href(item.href);
|
|
139
|
+
const mark = img ? (_jsx("img", { src: img, alt: name, loading: "lazy", decoding: "async", className: `forge-site-logo-img object-contain ${grid ? "h-10 w-32" : "h-9 w-32"}` })) : (_jsx("span", { className: "forge-heading forge-site-wordmark text-xl font-semibold tracking-tight whitespace-nowrap", children: name }));
|
|
140
|
+
return to ? (_jsx("a", { href: to, ...rel(to), className: "forge-focusable forge-site-logo inline-flex items-center justify-center rounded-sm", children: mark })) : (_jsx("span", { className: "forge-site-logo inline-flex items-center justify-center", children: mark }));
|
|
141
|
+
}
|
|
142
|
+
function Logos(ctx) {
|
|
143
|
+
const list = items(ctx.section.props.items).filter((i) => str(i.name));
|
|
144
|
+
if (!list.length)
|
|
145
|
+
return null;
|
|
146
|
+
const p = ctx.section.props;
|
|
147
|
+
if (ctx.section.variant === "grid") {
|
|
148
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("ul", { className: "forge-site-logo-grid grid grid-cols-2 @3xl:grid-cols-3 @5xl:grid-cols-6", children: list.map((l, i) => (_jsx("li", { className: "flex h-28 items-center justify-center p-6", children: _jsx(LogoMark, { item: l, grid: true }) }, i))) })] }));
|
|
149
|
+
}
|
|
150
|
+
// strip: a quiet line ("Trusted by…") over a row of marks.
|
|
151
|
+
const title = str(p.title);
|
|
152
|
+
return (_jsxs(Section, { ctx: ctx, pad: "py-12 @3xl:py-16", labelled: !!title, children: [(title || str(p.subtitle)) && (_jsxs("div", { className: "mx-auto mb-8 max-w-2xl text-center", children: [title && (_jsx("h2", { id: `${safeId(ctx.section.key)}-title`, className: "text-sm font-medium tracking-wide text-ink-muted", children: title })), str(p.subtitle) && _jsx("p", { className: "mt-2 text-sm text-ink-muted", children: String(p.subtitle) })] })), _jsx("ul", { className: "flex flex-wrap items-center justify-center gap-x-12 gap-y-8", children: list.map((l, i) => (_jsx("li", { children: _jsx(LogoMark, { item: l, grid: false }) }, i))) })] }));
|
|
153
|
+
}
|
|
154
|
+
// ─── Testimonials ───────────────────────────────────────────────────────────
|
|
155
|
+
function Testimonials(ctx) {
|
|
156
|
+
const list = items(ctx.section.props.items).filter((i) => str(i.quote) && str(i.author));
|
|
157
|
+
if (!list.length)
|
|
158
|
+
return null;
|
|
159
|
+
if (ctx.section.variant === "single") {
|
|
160
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("ul", { className: "grid gap-20", children: list.map((q, i) => (_jsx("li", { children: _jsxs("figure", { className: "mx-auto max-w-4xl text-center", children: [_jsx(Quote, { className: "forge-site-quote-mark mx-auto size-10", "aria-hidden": true }), _jsx("blockquote", { className: "forge-heading mt-6 text-2xl leading-snug font-medium tracking-tight text-balance whitespace-pre-line text-ink @3xl:text-3xl", children: String(q.quote) }), _jsxs("figcaption", { className: "mt-8 flex items-center justify-center gap-3", children: [_jsx(Avatar, { src: image(q.avatar), name: String(q.author), className: "size-12" }), _jsxs("span", { className: "text-left", children: [_jsx("span", { className: "block font-semibold text-ink", children: String(q.author) }), str(q.role) && _jsx("span", { className: "block text-sm text-ink-muted", children: String(q.role) })] })] })] }) }, i))) })] }));
|
|
161
|
+
}
|
|
162
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("ul", { className: "gap-5 @3xl:columns-2 @5xl:columns-3", children: list.map((q, i) => (_jsx("li", { className: "mb-5 break-inside-avoid", children: _jsxs("figure", { className: "forge-card forge-site-card flex flex-col p-[var(--forge-pad-card-lg)]", children: [_jsx(Quote, { className: "forge-site-quote-mark size-6", "aria-hidden": true }), _jsx("blockquote", { className: "mt-4 leading-relaxed text-pretty whitespace-pre-line text-ink", children: String(q.quote) }), _jsxs("figcaption", { className: "mt-6 flex items-center gap-3 border-t border-line pt-5", children: [_jsx(Avatar, { src: image(q.avatar), name: String(q.author) }), _jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold text-ink", children: String(q.author) }), str(q.role) && _jsx("span", { className: "block text-sm text-ink-muted", children: String(q.role) })] })] })] }) }, i))) })] }));
|
|
163
|
+
}
|
|
164
|
+
// ─── Stats ──────────────────────────────────────────────────────────────────
|
|
165
|
+
function Stats(ctx) {
|
|
166
|
+
const list = items(ctx.section.props.items).filter((i) => str(i.label));
|
|
167
|
+
if (!list.length)
|
|
168
|
+
return null;
|
|
169
|
+
const live = ctx.data?.kind === "stats" ? ctx.data.values : [];
|
|
170
|
+
const value = (item, i) => {
|
|
171
|
+
const v = live[i] ?? str(item.value);
|
|
172
|
+
return v ? `${str(item.prefix) ?? ""}${v}${str(item.suffix) ?? ""}` : "—";
|
|
173
|
+
};
|
|
174
|
+
if (ctx.section.variant === "cards") {
|
|
175
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("dl", { className: `grid gap-5 @md:grid-cols-2 ${cols(list.length, 4)}`, children: list.map((s, i) => (_jsxs("div", { className: "forge-card forge-metric forge-site-card flex flex-col-reverse justify-end gap-2 p-[var(--forge-pad-card-lg)]", children: [_jsx("dt", { className: "text-sm text-ink-muted", children: String(s.label) }), _jsx("dd", { className: "forge-heading forge-site-stat text-4xl font-semibold tracking-tight tabular-nums", children: value(s, i) })] }, i))) })] }));
|
|
176
|
+
}
|
|
177
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("dl", { className: `forge-site-stats-row grid grid-cols-2 gap-y-10 ${cols(list.length, 4)}`, children: list.map((s, i) => (_jsxs("div", { className: "flex flex-col-reverse items-center gap-2 px-4 text-center", children: [_jsx("dt", { className: "text-sm text-ink-muted", children: String(s.label) }), _jsx("dd", { className: "forge-heading forge-site-stat text-4xl font-semibold tracking-tight tabular-nums @3xl:text-5xl", children: value(s, i) })] }, i))) })] }));
|
|
178
|
+
}
|
|
179
|
+
// ─── Steps ──────────────────────────────────────────────────────────────────
|
|
180
|
+
function Steps(ctx) {
|
|
181
|
+
const list = items(ctx.section.props.items).filter((i) => str(i.title));
|
|
182
|
+
if (!list.length)
|
|
183
|
+
return null;
|
|
184
|
+
const H = itemTag(ctx);
|
|
185
|
+
if (ctx.section.variant === "timeline") {
|
|
186
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: "max-w-3xl", children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ol", { className: "forge-site-timeline grid", children: list.map((s, i) => (_jsxs("li", { className: "forge-site-timeline-item relative grid grid-cols-[2.5rem_1fr] gap-x-5 pb-10 last:pb-0", children: [_jsx("span", { className: "forge-site-step-number relative z-10 flex size-10 items-center justify-center text-sm font-semibold", "aria-hidden": true, children: i + 1 }), _jsxs("div", { className: "pt-1.5", children: [_jsx(H, { className: "forge-heading text-lg font-semibold text-ink", children: String(s.title) }), str(s.description) && _jsx("p", { className: "mt-2 leading-relaxed text-pretty whitespace-pre-line text-ink-muted", children: String(s.description) })] })] }, i))) })] }));
|
|
187
|
+
}
|
|
188
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ol", { className: `grid gap-5 @md:grid-cols-2 ${list.length % 3 === 0 || list.length === 5 ? "@5xl:grid-cols-3" : "@5xl:grid-cols-4"}`, children: list.map((s, i) => (_jsxs("li", { className: "forge-card forge-site-card flex flex-col p-[var(--forge-pad-card-lg)]", children: [_jsx("span", { className: "forge-site-step-number flex size-10 items-center justify-center text-sm font-semibold", "aria-hidden": true, children: i + 1 }), _jsx(H, { className: "forge-heading mt-5 text-lg font-semibold text-ink", children: String(s.title) }), str(s.description) && _jsx("p", { className: "mt-2 text-sm leading-relaxed text-pretty whitespace-pre-line text-ink-muted", children: String(s.description) })] }, i))) })] }));
|
|
189
|
+
}
|
|
190
|
+
/** The payments plugin's plans (render data, else its options in the config). */
|
|
191
|
+
function paymentPlans(ctx) {
|
|
192
|
+
const options = ctx.app.plugins.find((p) => p.name === "payments")?.options;
|
|
193
|
+
const configured = Array.isArray(options?.plans) ? options.plans : [];
|
|
194
|
+
const source = ctx.data?.kind === "plans" ? ctx.data.plans : configured.map((p) => ({ id: p.id, name: p.name, price: p.price ?? null, description: p.description ?? null, features: p.features ?? [] }));
|
|
195
|
+
const signUp = ctx.app.auth.registration ? { label: ctx.t("site.pricing.getStarted"), href: "/sign-up" } : { label: ctx.t("site.signIn"), href: "/sign-in" };
|
|
196
|
+
return source.map((p, i) => {
|
|
197
|
+
const free = configured.find((c) => c.id === p.id && !c.priceEnv);
|
|
198
|
+
const price = p.price ?? (free ? ctx.t("site.pricing.free") : undefined);
|
|
199
|
+
return { name: p.name, ...(price ? { price } : {}), ...(p.description ? { description: p.description } : {}), features: strings(p.features), cta: signUp, highlighted: source.length >= 3 && i === 1 };
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function Pricing(ctx) {
|
|
203
|
+
const p = ctx.section.props;
|
|
204
|
+
const plans = p.source === "payments"
|
|
205
|
+
? paymentPlans(ctx)
|
|
206
|
+
: items(p.plans)
|
|
207
|
+
.filter((x) => str(x.name))
|
|
208
|
+
.map((x) => ({ name: String(x.name), price: str(x.price), period: str(x.period), description: str(x.description), features: strings(x.features), cta: link(x.cta), highlighted: x.highlighted === true }));
|
|
209
|
+
if (!plans.length)
|
|
210
|
+
return null;
|
|
211
|
+
const H = itemTag(ctx);
|
|
212
|
+
const popular = ctx.t("site.pricing.popular");
|
|
213
|
+
if (ctx.section.variant === "compact") {
|
|
214
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: "max-w-5xl", children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("ul", { className: "forge-card forge-site-card divide-y divide-line overflow-hidden", children: plans.map((plan, i) => (_jsxs("li", { className: `grid items-center gap-x-8 gap-y-4 p-6 @3xl:grid-cols-[minmax(0,1fr)_auto_auto] @3xl:px-8 ${plan.highlighted ? "forge-site-plan-row-highlight" : ""}`, children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(H, { className: "forge-heading text-lg font-semibold text-ink", children: plan.name }), plan.highlighted && _jsx("span", { className: "forge-chip px-2 py-0.5 text-xs font-semibold", children: popular })] }), plan.description && _jsx("p", { className: "mt-1 text-sm text-pretty text-ink-muted", children: plan.description }), plan.features.length > 0 && (_jsx("ul", { className: "mt-3 flex flex-wrap gap-x-5 gap-y-1.5 text-sm text-ink-muted", children: plan.features.map((f, j) => (_jsxs("li", { className: "inline-flex items-center gap-1.5", children: [_jsx(Check, { className: "forge-site-check size-3.5 shrink-0", "aria-hidden": true }), f] }, j))) }))] }), plan.price && (_jsxs("p", { className: "text-ink", children: [_jsx("span", { className: "forge-heading text-2xl font-semibold tracking-tight", children: plan.price }), plan.period && _jsx("span", { className: "ml-1 text-sm text-ink-muted", children: plan.period })] })), plan.cta && (_jsx("a", { href: plan.cta.href, ...rel(plan.cta.href), className: `forge-btn ${plan.highlighted ? "forge-btn-primary" : "forge-btn-secondary"} inline-flex h-10 items-center justify-center px-4 text-sm font-medium`, children: plan.cta.label }))] }, i))) })] }));
|
|
215
|
+
}
|
|
216
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("ul", { className: `mx-auto grid items-stretch gap-5 ${plans.length <= 2 ? "max-w-4xl" : ""} @md:grid-cols-2 ${plans.length >= 3 ? "@5xl:grid-cols-3" : ""}`, children: plans.map((plan, i) => (_jsxs("li", { className: `forge-card forge-site-card forge-site-plan relative flex flex-col p-7 ${plan.highlighted ? "forge-site-plan-highlight" : ""}`, children: [plan.highlighted && _jsx("span", { className: "forge-site-plan-badge absolute -top-3 left-7 px-2.5 py-0.5 text-xs font-semibold", children: popular }), _jsx(H, { className: "forge-heading text-lg font-semibold text-ink", children: plan.name }), plan.description && _jsx("p", { className: "mt-2 text-sm leading-relaxed text-pretty text-ink-muted", children: plan.description }), plan.price && (_jsxs("p", { className: "mt-6 flex items-baseline gap-1.5 text-ink", children: [_jsx("span", { className: "forge-heading text-4xl font-semibold tracking-tight", children: plan.price }), plan.period && _jsx("span", { className: "text-sm text-ink-muted", children: plan.period })] })), plan.cta && (_jsx("a", { href: plan.cta.href, ...rel(plan.cta.href), className: `forge-btn ${plan.highlighted ? "forge-btn-primary" : "forge-btn-secondary"} mt-6 inline-flex h-11 w-full items-center justify-center px-4 text-base font-medium`, children: plan.cta.label })), plan.features.length > 0 && (_jsx("ul", { className: "mt-7 grid gap-3 border-t border-line pt-6 text-sm text-ink", children: plan.features.map((f, j) => (_jsxs("li", { className: "flex gap-2.5", children: [_jsx(Check, { className: "forge-site-check mt-0.5 size-4 shrink-0", "aria-hidden": true }), _jsx("span", { children: f })] }, j))) }))] }, i))) })] }));
|
|
217
|
+
}
|
|
218
|
+
// ─── FAQ ────────────────────────────────────────────────────────────────────
|
|
219
|
+
function Faq(ctx) {
|
|
220
|
+
const list = items(ctx.section.props.items).filter((i) => str(i.question) && str(i.answer));
|
|
221
|
+
if (!list.length)
|
|
222
|
+
return null;
|
|
223
|
+
if (ctx.section.variant === "columns") {
|
|
224
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("dl", { className: "grid gap-x-12 gap-y-10 @3xl:grid-cols-2", children: list.map((q, i) => (_jsxs("div", { children: [_jsx("dt", { className: "forge-heading text-lg font-semibold text-balance text-ink", children: String(q.question) }), _jsx("dd", { className: "mt-3 text-ink-muted", children: _jsx(Markdown, { source: String(q.answer), minHeading: hasTitle(ctx) ? 4 : 3 }) })] }, i))) })] }));
|
|
225
|
+
}
|
|
226
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: "max-w-3xl", children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("div", { className: "forge-site-faq border-y border-line", children: list.map((q, i) => (_jsxs("details", { className: "forge-site-faq-item border-b border-line last:border-b-0", children: [_jsxs("summary", { className: "forge-focusable flex cursor-pointer list-none items-center justify-between gap-6 rounded-sm py-5 text-left text-lg font-medium text-ink", children: [_jsx("span", { className: "text-balance", children: String(q.question) }), _jsx("span", { className: "forge-site-faq-icon flex size-7 shrink-0 items-center justify-center rounded-full", "aria-hidden": true, children: _jsx(Plus, { className: "size-4" }) })] }), _jsx("div", { className: "pr-12 pb-6 text-ink-muted", children: _jsx(Markdown, { source: String(q.answer), minHeading: hasTitle(ctx) ? 3 : 2 }) })] }, i))) })] }));
|
|
227
|
+
}
|
|
228
|
+
// ─── Call to action ─────────────────────────────────────────────────────────
|
|
229
|
+
function Cta(ctx) {
|
|
230
|
+
const p = ctx.section.props;
|
|
231
|
+
const title = str(p.title);
|
|
232
|
+
if (!title)
|
|
233
|
+
return null;
|
|
234
|
+
const id = `${safeId(ctx.section.key)}-title`;
|
|
235
|
+
const subtitle = str(p.subtitle);
|
|
236
|
+
const primary = link(p.primary);
|
|
237
|
+
const secondary = link(p.secondary);
|
|
238
|
+
if (ctx.section.variant === "split") {
|
|
239
|
+
return (_jsx(Section, { ctx: ctx, children: _jsxs("div", { className: "forge-card forge-site-cta-split flex flex-col items-start justify-between gap-8 p-8 @3xl:flex-row @3xl:items-center @3xl:p-12", children: [_jsxs("div", { className: "max-w-2xl", children: [_jsx("h2", { id: id, className: "forge-heading text-2xl font-semibold tracking-tight text-balance text-ink @3xl:text-3xl", children: title }), subtitle && _jsx("p", { className: "mt-3 text-lg text-pretty whitespace-pre-line text-ink-muted", children: subtitle })] }), _jsx(Buttons, { primary: primary, secondary: secondary, className: "shrink-0" })] }) }));
|
|
240
|
+
}
|
|
241
|
+
if (ctx.section.variant === "minimal") {
|
|
242
|
+
return (_jsx(Section, { ctx: ctx, width: "max-w-3xl", children: _jsxs("div", { className: "text-center", children: [_jsx("h2", { id: id, className: "forge-heading text-3xl font-semibold tracking-tight text-balance text-ink @3xl:text-4xl", children: title }), subtitle && _jsx("p", { className: "mt-4 text-lg text-pretty whitespace-pre-line text-ink-muted", children: subtitle }), _jsx(Buttons, { primary: primary, secondary: secondary, center: true, className: "mt-8" })] }) }));
|
|
243
|
+
}
|
|
244
|
+
// banner: the brand mesh of the sign-in pages.
|
|
245
|
+
return (_jsx(Section, { ctx: ctx, children: _jsxs("div", { className: "forge-auth-brand forge-landing-hero forge-site-brand relative overflow-hidden px-6 py-14 text-center @3xl:px-12 @3xl:py-20", style: brandMeshStyle(`${ctx.app.name}:${ctx.section.key}`), children: [_jsx(BrandPattern, { name: ctx.app.name, id: `forge-site-dots-${safeId(ctx.section.key)}` }), _jsxs("div", { className: "forge-auth-copy relative mx-auto max-w-2xl", children: [_jsx("h2", { id: id, className: "forge-heading text-3xl font-semibold tracking-tight text-balance @3xl:text-4xl", children: title }), subtitle && _jsx("p", { className: "mt-4 text-lg text-pretty whitespace-pre-line opacity-90", children: subtitle }), _jsx(Buttons, { primary: primary, secondary: secondary, onBrand: true, center: true, className: "mt-8" })] })] }) }));
|
|
246
|
+
}
|
|
247
|
+
// ─── Comparison ─────────────────────────────────────────────────────────────
|
|
248
|
+
function Cell({ value, t }) {
|
|
249
|
+
if (value === true)
|
|
250
|
+
return (_jsxs(_Fragment, { children: [_jsx(Check, { className: "forge-site-check inline size-5", "aria-hidden": true }), _jsx("span", { className: "sr-only", children: t("site.value.yes") })] }));
|
|
251
|
+
if (value === false)
|
|
252
|
+
return (_jsxs(_Fragment, { children: [_jsx(Minus, { className: "inline size-5 text-ink-subtle", "aria-hidden": true }), _jsx("span", { className: "sr-only", children: t("site.value.no") })] }));
|
|
253
|
+
return typeof value === "string" ? _jsx(_Fragment, { children: value }) : null;
|
|
254
|
+
}
|
|
255
|
+
function Comparison(ctx) {
|
|
256
|
+
const p = ctx.section.props;
|
|
257
|
+
const columns = strings(p.columns);
|
|
258
|
+
const rows = items(p.rows).filter((r) => str(r.feature));
|
|
259
|
+
if (!columns.length || !rows.length)
|
|
260
|
+
return null;
|
|
261
|
+
const values = (r) => (Array.isArray(r.values) ? r.values : []);
|
|
262
|
+
if (ctx.section.variant === "cards") {
|
|
263
|
+
const H = itemTag(ctx);
|
|
264
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("ul", { className: `grid gap-5 @md:grid-cols-2 ${cols(columns.length, 4)}`, children: columns.map((c, j) => (_jsxs("li", { className: `forge-card forge-site-card flex flex-col p-[var(--forge-pad-card-lg)] ${j === 0 ? "forge-site-plan-highlight" : ""}`, children: [_jsx(H, { className: "forge-heading text-lg font-semibold text-ink", children: c }), _jsx("ul", { className: "mt-5 grid gap-3 text-sm", children: rows.map((r, i) => {
|
|
265
|
+
const v = values(r)[j];
|
|
266
|
+
return (_jsxs("li", { className: `flex items-start gap-2.5 ${v === false ? "text-ink-muted" : "text-ink"}`, children: [typeof v === "string" ? _jsx(Check, { className: "forge-site-check mt-0.5 size-4 shrink-0", "aria-hidden": true }) : _jsx("span", { className: "shrink-0 [&_svg]:size-4", children: _jsx(Cell, { value: v, t: ctx.t }) }), _jsxs("span", { children: [String(r.feature), typeof v === "string" && v && _jsxs("span", { className: "text-ink-muted", children: [" \u2014 ", v] })] })] }, i));
|
|
267
|
+
}) })] }, j))) })] }));
|
|
268
|
+
}
|
|
269
|
+
const title = str(p.title);
|
|
270
|
+
return (_jsxs(Section, { ctx: ctx, labelled: !!title, width: "max-w-5xl", children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("div", { className: "forge-card forge-site-card overflow-hidden", children: _jsx("div", { className: "forge-site-table-scroll", role: "region", tabIndex: 0, "aria-label": title ?? ctx.t("site.comparison.caption"), children: _jsxs("table", { className: "forge-site-compare w-full min-w-[34rem] text-left text-sm", children: [_jsx("caption", { className: "sr-only", children: title ?? ctx.t("site.comparison.caption") }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", className: "px-5 py-4 font-medium text-ink-muted", children: ctx.t("site.comparison.feature") }), columns.map((c, j) => (_jsx("th", { scope: "col", className: "px-5 py-4 text-center font-semibold text-ink", "data-highlight": j === 0 || undefined, children: c }, j)))] }) }), _jsx("tbody", { children: rows.map((r, i) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", className: "px-5 py-3.5 font-medium text-ink", children: String(r.feature) }), columns.map((_, j) => (_jsx("td", { className: "px-5 py-3.5 text-center text-ink-muted", "data-highlight": j === 0 || undefined, children: _jsx(Cell, { value: values(r)[j], t: ctx.t }) }, j)))] }, i))) })] }) }) })] }));
|
|
271
|
+
}
|
|
272
|
+
// ─── Integrations ───────────────────────────────────────────────────────────
|
|
273
|
+
function LogoTile({ name, src, className = "size-12" }) {
|
|
274
|
+
return (_jsx("span", { className: `forge-site-logo-tile flex shrink-0 items-center justify-center overflow-hidden ${className}`, "aria-hidden": true, children: src ? (_jsx("img", { src: src, alt: "", loading: "lazy", decoding: "async", className: "size-3/4 object-contain" })) : (_jsx("span", { className: "forge-heading text-lg font-semibold", children: initials(name).slice(0, 1) })) }));
|
|
275
|
+
}
|
|
276
|
+
function Integrations(ctx) {
|
|
277
|
+
const list = items(ctx.section.props.items).filter((i) => str(i.name));
|
|
278
|
+
if (!list.length)
|
|
279
|
+
return null;
|
|
280
|
+
const H = itemTag(ctx);
|
|
281
|
+
if (ctx.section.variant === "list") {
|
|
282
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: "max-w-4xl", children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: "forge-card forge-site-card divide-y divide-line overflow-hidden", children: list.map((x, i) => {
|
|
283
|
+
const to = href(x.href);
|
|
284
|
+
const body = (_jsxs(_Fragment, { children: [_jsx(LogoTile, { name: String(x.name), src: image(x.image), className: "size-11" }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx(H, { className: "text-base font-semibold text-ink", children: String(x.name) }), str(x.description) && _jsx("span", { className: "mt-0.5 block text-sm text-pretty text-ink-muted", children: String(x.description) })] }), to && _jsx(ArrowUpRight, { className: "forge-site-arrow size-4 shrink-0 text-ink-muted", "aria-hidden": true })] }));
|
|
285
|
+
return (_jsx("li", { children: to ? (_jsx("a", { href: to, ...rel(to), className: "forge-focusable forge-site-row flex items-center gap-4 px-5 py-4", children: body })) : (_jsx("div", { className: "flex items-center gap-4 px-5 py-4", children: body })) }, i));
|
|
286
|
+
}) })] }));
|
|
287
|
+
}
|
|
288
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: "grid gap-4 @md:grid-cols-2 @3xl:grid-cols-3 @5xl:grid-cols-4", children: list.map((x, i) => {
|
|
289
|
+
const to = href(x.href);
|
|
290
|
+
const body = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx(LogoTile, { name: String(x.name), src: image(x.image) }), to && _jsx(ArrowUpRight, { className: "forge-site-arrow size-4 text-ink-muted", "aria-hidden": true })] }), _jsx(H, { className: "forge-heading mt-5 text-base font-semibold text-ink", children: String(x.name) }), str(x.description) && _jsx("p", { className: "mt-1.5 text-sm leading-relaxed text-pretty text-ink-muted", children: String(x.description) })] }));
|
|
291
|
+
return (_jsx("li", { className: "flex", children: to ? (_jsx("a", { href: to, ...rel(to), className: "forge-focusable flex w-full rounded-[var(--forge-radius)]", children: _jsx("span", { className: "forge-card forge-site-card forge-card-interactive flex w-full flex-col p-5", children: body }) })) : (_jsx("div", { className: "forge-card forge-site-card flex w-full flex-col p-5", children: body })) }, i));
|
|
292
|
+
}) })] }));
|
|
293
|
+
}
|
|
294
|
+
// ─── Team ───────────────────────────────────────────────────────────────────
|
|
295
|
+
function Team(ctx) {
|
|
296
|
+
const list = items(ctx.section.props.items).filter((i) => str(i.name));
|
|
297
|
+
if (!list.length)
|
|
298
|
+
return null;
|
|
299
|
+
const H = itemTag(ctx);
|
|
300
|
+
if (ctx.section.variant === "compact") {
|
|
301
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: "grid gap-x-8 gap-y-6 @md:grid-cols-2 @3xl:grid-cols-3 @5xl:grid-cols-4", children: list.map((m, i) => {
|
|
302
|
+
const to = href(m.href);
|
|
303
|
+
const name = String(m.name);
|
|
304
|
+
return (_jsxs("li", { className: "flex items-center gap-4", children: [_jsx(Avatar, { src: image(m.avatar), name: name, className: "size-14 rounded-full" }), _jsxs("div", { className: "min-w-0", children: [_jsx(H, { className: "font-semibold text-ink", children: to ? (_jsx("a", { href: to, ...rel(to), className: "forge-focusable rounded-sm hover:underline", children: name })) : (name) }), str(m.role) && _jsx("p", { className: "text-sm text-ink-muted", children: String(m.role) })] })] }, i));
|
|
305
|
+
}) })] }));
|
|
306
|
+
}
|
|
307
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: "grid gap-6 @md:grid-cols-2 @3xl:grid-cols-3 @5xl:grid-cols-4", children: list.map((m, i) => {
|
|
308
|
+
const to = href(m.href);
|
|
309
|
+
const name = String(m.name);
|
|
310
|
+
const avatar = image(m.avatar);
|
|
311
|
+
return (_jsxs("li", { className: "forge-card forge-site-card flex flex-col overflow-hidden", children: [avatar ? (_jsx(Media, { src: avatar, alt: "", ratio: "aspect-square", className: "forge-site-card-media" })) : (_jsx("div", { "aria-hidden": true, className: "forge-site-iconpanel flex aspect-square items-center justify-center", children: _jsx("span", { className: "forge-heading text-5xl font-semibold opacity-80", children: initials(name) }) })), _jsxs("div", { className: "flex flex-1 flex-col p-5", children: [_jsx(H, { className: "forge-heading text-lg font-semibold text-ink", children: name }), str(m.role) && _jsx("p", { className: "forge-site-role mt-0.5 text-sm font-medium", children: String(m.role) }), str(m.bio) && _jsx("p", { className: "mt-3 text-sm leading-relaxed text-pretty whitespace-pre-line text-ink-muted", children: String(m.bio) }), to && _jsx(MoreLink, { to: to, label: ctx.t("site.team.profile"), name: name, className: "mt-auto pt-4" })] })] }, i));
|
|
312
|
+
}) })] }));
|
|
313
|
+
}
|
|
314
|
+
// ─── Gallery ────────────────────────────────────────────────────────────────
|
|
315
|
+
function Gallery(ctx) {
|
|
316
|
+
const list = items(ctx.section.props.items).filter((i) => image(i.image));
|
|
317
|
+
if (!list.length)
|
|
318
|
+
return null;
|
|
319
|
+
const masonry = ctx.section.variant === "masonry";
|
|
320
|
+
// Masonry: a fixed rhythm of ratios so the columns interlock without knowing the images' sizes (no layout shift).
|
|
321
|
+
const ratios = ["aspect-[3/4]", "aspect-[4/3]", "aspect-square", "aspect-[4/5]", "aspect-[16/10]"];
|
|
322
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: masonry ? "gap-4 @md:columns-2 @3xl:columns-3" : "grid gap-4 @md:grid-cols-2 @3xl:grid-cols-3", children: list.map((g, i) => (_jsx("li", { className: masonry ? "mb-4 break-inside-avoid" : "", children: _jsxs("figure", { children: [_jsx(Media, { src: String(g.image), alt: str(g.alt) ?? "", ratio: masonry ? ratios[i % ratios.length] : "aspect-[4/3]", className: "forge-site-frame" }), str(g.caption) && _jsx("figcaption", { className: "mt-2.5 text-sm text-pretty text-ink-muted", children: String(g.caption) })] }) }, i))) })] }));
|
|
323
|
+
}
|
|
324
|
+
// ─── Contact and newsletter ─────────────────────────────────────────────────
|
|
325
|
+
/** Every word of the forms, translated here (the forms are client components). */
|
|
326
|
+
export function siteFormLabels(t) {
|
|
327
|
+
return {
|
|
328
|
+
name: t("site.form.name"),
|
|
329
|
+
email: t("site.form.email"),
|
|
330
|
+
company: t("site.form.company"),
|
|
331
|
+
message: t("site.form.message"),
|
|
332
|
+
optional: t("site.form.optional"),
|
|
333
|
+
submit: t("site.form.send"),
|
|
334
|
+
sending: t("site.form.sending"),
|
|
335
|
+
success: t("site.form.success"),
|
|
336
|
+
required: t("site.form.required"),
|
|
337
|
+
invalidEmail: t("site.form.invalidEmail"),
|
|
338
|
+
tooLong: t("site.form.tooLong"),
|
|
339
|
+
messageTooShort: t("site.form.messageTooShort"),
|
|
340
|
+
error: t("site.form.error"),
|
|
341
|
+
rateLimited: t("site.form.rateLimited"),
|
|
342
|
+
honeypot: t("site.form.honeypot"),
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function ContactDetails({ ctx }) {
|
|
346
|
+
const p = ctx.section.props;
|
|
347
|
+
const email = str(p.email);
|
|
348
|
+
const phone = str(p.phone);
|
|
349
|
+
const address = str(p.address);
|
|
350
|
+
const tel = phone ? `tel:${phone.replace(/[^\d+]/g, "")}` : undefined;
|
|
351
|
+
if (!email && !phone && !address)
|
|
352
|
+
return null;
|
|
353
|
+
const row = (icon, label, body) => (_jsxs("li", { className: "flex items-start gap-3.5", children: [_jsx("span", { className: "forge-chip mt-0.5 flex size-9 shrink-0 items-center justify-center", "aria-hidden": true, children: icon }), _jsxs("span", { children: [_jsx("span", { className: "block text-sm text-ink-muted", children: label }), _jsx("span", { className: "mt-0.5 block font-medium text-ink", children: body })] })] }));
|
|
354
|
+
return (_jsxs("ul", { className: "grid gap-5", children: [email &&
|
|
355
|
+
validHref(`mailto:${email}`) &&
|
|
356
|
+
row(_jsx(Mail, { className: "size-4" }), ctx.t("site.contact.email"), _jsx("a", { href: `mailto:${email}`, className: "forge-focusable rounded-sm hover:underline", children: email })), phone &&
|
|
357
|
+
row(_jsx(Phone, { className: "size-4" }), ctx.t("site.contact.phone"), tel && validHref(tel) ? (_jsx("a", { href: tel, className: "forge-focusable rounded-sm hover:underline", children: phone })) : (phone)), address && row(_jsx(MapPin, { className: "size-4" }), ctx.t("site.contact.address"), _jsx("address", { className: "not-italic whitespace-pre-line", children: address }))] }));
|
|
358
|
+
}
|
|
359
|
+
function Contact(ctx) {
|
|
360
|
+
const p = ctx.section.props;
|
|
361
|
+
const form = _jsx(ContactForm, { labels: siteFormLabels(ctx.t), company: p.company === true, buttonLabel: str(p.buttonLabel), successMessage: str(p.successMessage), page: ctx.pagePath, disabled: ctx.preview });
|
|
362
|
+
if (ctx.section.variant === "split") {
|
|
363
|
+
return (_jsx(Section, { ctx: ctx, labelled: hasTitle(ctx), children: _jsxs("div", { className: "grid gap-12 @3xl:grid-cols-[minmax(0,1fr)_minmax(0,1.35fr)] @5xl:gap-20", children: [_jsxs("div", { children: [_jsx(SectionHeading, { ctx: ctx, className: "mb-10" }), _jsx(ContactDetails, { ctx: ctx })] }), _jsx("div", { className: "forge-card forge-site-card p-6 @3xl:p-10", children: form })] }) }));
|
|
364
|
+
}
|
|
365
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: "max-w-2xl", children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("div", { className: "forge-card forge-site-card p-6 @3xl:p-10", children: form }), (str(p.email) || str(p.phone) || str(p.address)) && (_jsx("div", { className: "mt-10", children: _jsx(ContactDetails, { ctx: ctx }) }))] }));
|
|
366
|
+
}
|
|
367
|
+
function Newsletter(ctx) {
|
|
368
|
+
const p = ctx.section.props;
|
|
369
|
+
const labels = siteFormLabels(ctx.t);
|
|
370
|
+
const form = (layout) => _jsx(NewsletterForm, { labels: { ...labels, submit: ctx.t("site.newsletter.subscribe") }, buttonLabel: str(p.buttonLabel), successMessage: str(p.successMessage) ?? ctx.t("site.newsletter.success"), source: ctx.pagePath, layout: layout, disabled: ctx.preview });
|
|
371
|
+
if (ctx.section.variant === "card") {
|
|
372
|
+
return (_jsx(Section, { ctx: ctx, labelled: hasTitle(ctx), width: "max-w-2xl", children: _jsxs("div", { className: "forge-card forge-site-card p-8 text-center @3xl:p-12", children: [_jsx(SectionHeading, { ctx: ctx, center: true, className: "mb-8" }), form("row")] }) }));
|
|
373
|
+
}
|
|
374
|
+
return (_jsx(Section, { ctx: ctx, labelled: hasTitle(ctx), children: _jsxs("div", { className: "forge-site-panel grid items-center gap-8 p-8 @3xl:grid-cols-[minmax(0,1.1fr)_minmax(0,1fr)] @3xl:p-12", children: [_jsx(SectionHeading, { ctx: ctx, className: "mb-0" }), form("row")] }) }));
|
|
375
|
+
}
|
|
376
|
+
// ─── Content ────────────────────────────────────────────────────────────────
|
|
377
|
+
function RichText(ctx) {
|
|
378
|
+
const body = str(ctx.section.props.body);
|
|
379
|
+
if (!body)
|
|
380
|
+
return null;
|
|
381
|
+
const narrow = ctx.section.variant === "narrow";
|
|
382
|
+
return (_jsx(Section, { ctx: ctx, labelled: false, width: narrow ? "max-w-2xl" : "max-w-3xl", pad: "py-12 @3xl:py-16", children: _jsx(Markdown, { source: body, className: narrow ? "forge-site-prose-lg" : "" }) }));
|
|
383
|
+
}
|
|
384
|
+
function ImageText(ctx) {
|
|
385
|
+
const p = ctx.section.props;
|
|
386
|
+
const img = image(p.image);
|
|
387
|
+
const title = str(p.title);
|
|
388
|
+
const body = str(p.body);
|
|
389
|
+
const cta = link(p.cta);
|
|
390
|
+
if (!img && !title && !body)
|
|
391
|
+
return null;
|
|
392
|
+
return (_jsx(Section, { ctx: ctx, labelled: !!title, children: _jsxs("div", { className: `grid items-center gap-10 ${img ? "@3xl:grid-cols-2 @5xl:gap-16" : ""}`, children: [img && _jsx(Media, { src: img, alt: str(p.imageAlt) ?? "", className: `forge-site-frame ${ctx.section.variant === "right" ? "@3xl:order-last" : ""}` }), _jsxs("div", { className: "max-w-xl", children: [str(p.eyebrow) && _jsx("p", { className: "forge-site-eyebrow mb-3 text-sm font-semibold", children: String(p.eyebrow) }), title && (_jsx("h2", { id: `${safeId(ctx.section.key)}-title`, className: "forge-heading text-3xl leading-tight font-semibold tracking-tight text-balance text-ink @3xl:text-4xl", children: title })), body && _jsx(Markdown, { source: body, className: `text-ink-muted ${title ? "mt-5" : ""}`, minHeading: title ? 3 : 2 }), cta && (_jsx("div", { className: "mt-8", children: _jsx(Button, { to: cta, tone: "primary", arrow: true }) }))] })] }) }));
|
|
393
|
+
}
|
|
394
|
+
function Video(ctx) {
|
|
395
|
+
const p = ctx.section.props;
|
|
396
|
+
const video = parseVideo(p.url);
|
|
397
|
+
if (!video)
|
|
398
|
+
return null;
|
|
399
|
+
const caption = str(p.caption);
|
|
400
|
+
const frameTitle = caption ?? str(p.title) ?? ctx.t("site.video.title");
|
|
401
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: ctx.section.variant === "wide" ? "max-w-6xl" : "max-w-4xl", children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsxs("figure", { children: [_jsx("div", { className: "forge-site-frame forge-site-media aspect-video overflow-hidden", children: video.kind === "file" ? (_jsx("video", { controls: true, preload: "metadata", className: "size-full bg-black object-contain", "aria-label": frameTitle, children: _jsx("source", { src: video.src }) })) : (_jsx("iframe", { src: video.kind === "youtube" ? `https://www.youtube-nocookie.com/embed/${video.id}` : `https://player.vimeo.com/video/${video.id}?dnt=1`, title: frameTitle, loading: "lazy", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true, referrerPolicy: "strict-origin-when-cross-origin", className: "size-full border-0" })) }), caption && _jsx("figcaption", { className: "mt-3 text-center text-sm text-ink-muted", children: caption })] })] }));
|
|
402
|
+
}
|
|
403
|
+
function Embed(ctx) {
|
|
404
|
+
const p = ctx.section.props;
|
|
405
|
+
const url = str(p.url);
|
|
406
|
+
const frameTitle = str(p.frameTitle);
|
|
407
|
+
if (!url || !frameTitle || !embedAllowed(url, ctx.app.site?.embedHosts ?? []))
|
|
408
|
+
return null;
|
|
409
|
+
const height = typeof p.height === "number" && Number.isFinite(p.height) ? Math.min(1200, Math.max(200, Math.round(p.height))) : 600;
|
|
410
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: ctx.section.variant === "wide" ? "max-w-6xl" : "max-w-4xl", children: [_jsx(SectionHeading, { ctx: ctx, center: true }), _jsx("div", { className: "forge-card forge-site-card overflow-hidden", children: _jsx("iframe", { src: url, title: frameTitle, loading: "lazy", sandbox: "allow-scripts allow-same-origin allow-forms allow-popups", referrerPolicy: "strict-origin-when-cross-origin", className: "block w-full border-0", style: { height: `${height}px` } }) })] }));
|
|
411
|
+
}
|
|
412
|
+
// ─── Posts ──────────────────────────────────────────────────────────────────
|
|
413
|
+
function postHref(post, base) {
|
|
414
|
+
const url = post.url ?? (base ? `${base}/${post.slug}` : undefined);
|
|
415
|
+
return url && validHref(url) ? url : undefined;
|
|
416
|
+
}
|
|
417
|
+
function PostMeta({ post, ctx, className = "" }) {
|
|
418
|
+
const date = post.publishedAt ? formatDate(post.publishedAt, ctx.lang) : "";
|
|
419
|
+
if (!date && !post.authorName)
|
|
420
|
+
return null;
|
|
421
|
+
return (_jsxs("p", { className: `flex flex-wrap items-center gap-x-2 text-sm text-ink-muted ${className}`.trim(), children: [date && _jsx("time", { dateTime: post.publishedAt, children: date }), date && post.authorName && _jsx("span", { "aria-hidden": true, children: "\u00B7" }), post.authorName && _jsx("span", { children: post.authorName })] }));
|
|
422
|
+
}
|
|
423
|
+
function PostCover({ post, ratio, className = "" }) {
|
|
424
|
+
const cover = image(post.coverImage);
|
|
425
|
+
if (cover)
|
|
426
|
+
return _jsx(Media, { src: cover, alt: "", ratio: ratio, className: className });
|
|
427
|
+
// No cover: a quiet decorative panel keeps the cards aligned.
|
|
428
|
+
return _jsx("div", { "aria-hidden": true, className: `forge-site-cover-fallback ${ratio} ${className}` });
|
|
429
|
+
}
|
|
430
|
+
function PostCard({ post, ctx, H, big = false }) {
|
|
431
|
+
const to = postHref(post, ctx.app.site?.blog?.[post.kind]);
|
|
432
|
+
return (_jsxs("article", { className: `forge-card forge-site-card forge-site-post group relative flex h-full flex-col overflow-hidden ${to ? "forge-card-interactive" : ""} ${big ? "@3xl:grid @3xl:grid-cols-2" : ""}`, children: [_jsx(PostCover, { post: post, ratio: big ? "aspect-[16/10] @3xl:aspect-auto @3xl:h-full" : "aspect-[16/9]", className: "forge-site-card-media" }), _jsxs("div", { className: `flex flex-1 flex-col ${big ? "p-7 @3xl:p-10" : "p-[var(--forge-pad-card-lg)]"}`, children: [post.tags.length > 0 && (_jsx("ul", { className: "mb-3 flex flex-wrap gap-1.5", children: post.tags.slice(0, 3).map((tag) => (_jsx("li", { className: "forge-chip px-2 py-0.5 text-xs font-medium", children: tag }, tag))) })), _jsx(H, { className: `forge-heading font-semibold tracking-tight text-balance text-ink ${big ? "text-2xl @3xl:text-3xl" : "text-lg"}`, children: to ? (_jsx("a", { href: to, className: "forge-focusable forge-site-stretched rounded-sm", children: post.title })) : (post.title) }), post.excerpt && _jsx("p", { className: `mt-2 leading-relaxed text-pretty text-ink-muted ${big ? "text-base @3xl:text-lg" : "line-clamp-3 text-sm"}`, children: post.excerpt }), _jsx(PostMeta, { post: post, ctx: ctx, className: "mt-auto pt-5" })] })] }));
|
|
433
|
+
}
|
|
434
|
+
function BlogList(ctx) {
|
|
435
|
+
const p = ctx.section.props;
|
|
436
|
+
const kind = (typeof p.kind === "string" ? p.kind : "post");
|
|
437
|
+
const posts = ctx.data?.kind === "posts" ? ctx.data.posts : [];
|
|
438
|
+
const base = ctx.app.site?.blog?.[kind];
|
|
439
|
+
const H = itemTag(ctx);
|
|
440
|
+
const rss = base && validHref(`${base}/rss.xml`) ? (_jsxs("a", { href: `${base}/rss.xml`, className: "forge-focusable mt-5 inline-flex items-center gap-1.5 rounded-sm text-sm font-medium text-link hover:underline", children: [_jsx(Rss, { className: "size-4", "aria-hidden": true }), ctx.t("site.blog.rss")] })) : null;
|
|
441
|
+
const heading = _jsx(SectionHeading, { ctx: ctx, children: rss });
|
|
442
|
+
if (!posts.length) {
|
|
443
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [heading, _jsx("p", { className: "forge-site-panel p-10 text-center text-ink-muted", children: ctx.t("site.blog.empty") })] }));
|
|
444
|
+
}
|
|
445
|
+
if (ctx.section.variant === "list") {
|
|
446
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: "max-w-4xl", children: [heading, _jsx("ul", { className: "divide-y divide-line border-y border-line", children: posts.map((post) => {
|
|
447
|
+
const to = postHref(post, ctx.app.site?.blog?.[post.kind]);
|
|
448
|
+
const cover = image(post.coverImage);
|
|
449
|
+
return (_jsx("li", { children: _jsxs("article", { className: "group relative grid gap-5 py-8 @3xl:grid-cols-[minmax(0,1fr)_11rem] @3xl:items-center", children: [_jsxs("div", { children: [_jsx(PostMeta, { post: post, ctx: ctx, className: "mb-2" }), _jsx(H, { className: "forge-heading text-xl font-semibold tracking-tight text-balance text-ink", children: to ? (_jsx("a", { href: to, className: "forge-focusable forge-site-stretched rounded-sm group-hover:text-link", children: post.title })) : (post.title) }), post.excerpt && _jsx("p", { className: "mt-2 leading-relaxed text-pretty text-ink-muted", children: post.excerpt })] }), cover && _jsx(Media, { src: cover, alt: "", ratio: "aspect-[4/3]", className: "forge-site-frame hidden @3xl:block" })] }) }, post.id));
|
|
450
|
+
}) })] }));
|
|
451
|
+
}
|
|
452
|
+
if (ctx.section.variant === "featured") {
|
|
453
|
+
const [lead, ...rest] = posts;
|
|
454
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [heading, _jsx(PostCard, { post: lead, ctx: ctx, H: H, big: true }), rest.length > 0 && (_jsx("ul", { className: "mt-6 grid gap-6 @md:grid-cols-2 @5xl:grid-cols-3", children: rest.map((post) => (_jsx("li", { children: _jsx(PostCard, { post: post, ctx: ctx, H: H }) }, post.id))) }))] }));
|
|
455
|
+
}
|
|
456
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [heading, _jsx("ul", { className: "grid gap-6 @md:grid-cols-2 @5xl:grid-cols-3", children: posts.map((post) => (_jsx("li", { children: _jsx(PostCard, { post: post, ctx: ctx, H: H }) }, post.id))) })] }));
|
|
457
|
+
}
|
|
458
|
+
// ─── Spacing ────────────────────────────────────────────────────────────────
|
|
459
|
+
function Spacer(ctx) {
|
|
460
|
+
const h = ctx.section.variant === "small" ? "h-8" : ctx.section.variant === "large" ? "h-28" : "h-16";
|
|
461
|
+
return _jsx("div", { "aria-hidden": true, className: `forge-site-spacer ${h}` });
|
|
462
|
+
}
|
|
463
|
+
function Divider(ctx) {
|
|
464
|
+
if (ctx.section.variant === "dots") {
|
|
465
|
+
return (_jsx("div", { role: "separator", className: "flex justify-center gap-2.5 py-8", children: [0, 1, 2].map((i) => (_jsx("span", { "aria-hidden": true, className: "forge-site-dot size-1.5 rounded-full" }, i))) }));
|
|
466
|
+
}
|
|
467
|
+
return (_jsx("div", { className: "px-4 py-4 @3xl:px-8", children: _jsx("hr", { className: "mx-auto max-w-6xl border-line" }) }));
|
|
468
|
+
}
|
|
469
|
+
// ─── Records ────────────────────────────────────────────────────────────────
|
|
470
|
+
function Records(ctx) {
|
|
471
|
+
const p = ctx.section.props;
|
|
472
|
+
const records = ctx.data?.kind === "records" ? ctx.data : undefined;
|
|
473
|
+
const entity = ctx.app.entities.find((e) => e.name === (records?.entity ?? p.entity));
|
|
474
|
+
if (!entity)
|
|
475
|
+
return null;
|
|
476
|
+
const fields = (records?.fields.length ? records.fields : strings(p.fields).length ? strings(p.fields) : [entity.display]).map((name) => ({ name, field: entityField(entity, name) }));
|
|
477
|
+
const rows = records?.rows ?? [];
|
|
478
|
+
const display = entityField(entity, entity.display);
|
|
479
|
+
const titleOf = (row) => formatFieldText(display, rowValue(row, display, entity.display), ctx.lang, ctx.t) || formatFieldText(fields[0]?.field, rowValue(row, fields[0]?.field, fields[0]?.name ?? ""), ctx.lang, ctx.t);
|
|
480
|
+
const label = (f) => f.field?.label ?? f.name;
|
|
481
|
+
const H = itemTag(ctx);
|
|
482
|
+
const value = (row, f) => _jsx(SiteFieldValue, { field: f.field, value: rowValue(row, f.field, f.name), locale: ctx.lang, t: ctx.t });
|
|
483
|
+
const filled = (row, f) => {
|
|
484
|
+
const v = rowValue(row, f.field, f.name);
|
|
485
|
+
return v !== null && v !== undefined && v !== "" && !(Array.isArray(v) && !v.length);
|
|
486
|
+
};
|
|
487
|
+
if (!rows.length) {
|
|
488
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("p", { className: "forge-site-panel p-10 text-center text-ink-muted", children: ctx.t("site.records.empty", { items: entity.pluralLabel }) })] }));
|
|
489
|
+
}
|
|
490
|
+
if (ctx.section.type === "entityList" && ctx.section.variant === "table") {
|
|
491
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("div", { className: "forge-card forge-site-card overflow-hidden", children: _jsx("div", { className: "forge-site-table-scroll", role: "region", tabIndex: 0, "aria-label": str(p.title) ?? entity.pluralLabel, children: _jsxs("table", { className: "forge-table w-full text-left text-sm", children: [_jsx("caption", { className: "sr-only", children: str(p.title) ?? entity.pluralLabel }), _jsx("thead", { children: _jsx("tr", { children: fields.map((f) => (_jsx("th", { scope: "col", className: "px-4 py-3 font-medium whitespace-nowrap", children: label(f) }, f.name))) }) }), _jsx("tbody", { children: rows.map((row, i) => (_jsx("tr", { children: fields.map((f, j) => (_jsx("td", { className: `px-4 py-3 ${j === 0 ? "font-medium text-ink" : "text-ink-muted"}`, children: f.field?.kind === "image" && fileOf(rowValue(row, f.field, f.name)) ? (_jsx("img", { src: fileOf(rowValue(row, f.field, f.name)).url, alt: "", loading: "lazy", decoding: "async", className: "forge-site-avatar size-9 object-cover" })) : (value(row, f)) }, f.name))) }, String(row.id ?? i)))) })] }) }) })] }));
|
|
492
|
+
}
|
|
493
|
+
const imageField = fields.find((f) => f.field?.kind === "image");
|
|
494
|
+
const details = fields.filter((f) => f !== imageField && f.field?.name !== entity.display);
|
|
495
|
+
if (ctx.section.type === "entityList") {
|
|
496
|
+
// list
|
|
497
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), width: "max-w-4xl", children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: "forge-card forge-site-card divide-y divide-line overflow-hidden", children: rows.map((row, i) => {
|
|
498
|
+
const img = imageField ? fileOf(rowValue(row, imageField.field, imageField.name)) : null;
|
|
499
|
+
return (_jsxs("li", { className: "flex items-start gap-4 px-5 py-4", children: [img && (_jsx("img", { src: img.url, alt: "", loading: "lazy", decoding: "async", className: "forge-site-avatar size-12 shrink-0 object-cover" })), _jsxs("div", { className: "min-w-0", children: [_jsx(H, { className: "font-semibold text-ink", children: titleOf(row) }), details.some((f) => filled(row, f)) && (_jsx("dl", { className: "mt-1.5 flex flex-wrap gap-x-5 gap-y-1 text-sm", children: details
|
|
500
|
+
.filter((f) => filled(row, f))
|
|
501
|
+
.map((f) => (_jsxs("div", { className: "flex gap-1.5", children: [_jsx("dt", { className: "text-ink-subtle", children: label(f) }), _jsx("dd", { className: "text-ink-muted", children: value(row, f) })] }, f.name))) }))] })] }, String(row.id ?? i)));
|
|
502
|
+
}) })] }));
|
|
503
|
+
}
|
|
504
|
+
const compact = ctx.section.variant === "compact";
|
|
505
|
+
return (_jsxs(Section, { ctx: ctx, labelled: hasTitle(ctx), children: [_jsx(SectionHeading, { ctx: ctx }), _jsx("ul", { className: `grid gap-5 @md:grid-cols-2 ${compact ? "@3xl:grid-cols-3 @5xl:grid-cols-4" : "@5xl:grid-cols-3"}`, children: rows.map((row, i) => {
|
|
506
|
+
const img = imageField ? fileOf(rowValue(row, imageField.field, imageField.name)) : null;
|
|
507
|
+
const shown = (compact ? details.slice(0, 2) : details).filter((f) => filled(row, f));
|
|
508
|
+
return (_jsxs("li", { className: "forge-card forge-site-card flex flex-col overflow-hidden", children: [img && !compact && _jsx(Media, { src: img.url, alt: "", ratio: "aspect-[4/3]", className: "forge-site-card-media" }), _jsxs("div", { className: compact ? "flex items-start gap-3 p-4" : "flex flex-1 flex-col p-[var(--forge-pad-card-lg)]", children: [img && compact && (_jsx("img", { src: img.url, alt: "", loading: "lazy", decoding: "async", className: "forge-site-avatar size-10 shrink-0 object-cover" })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(H, { className: `forge-heading font-semibold text-ink ${compact ? "text-base" : "text-lg"}`, children: titleOf(row) }), shown.length > 0 && (_jsx("dl", { className: compact ? "mt-1 grid gap-0.5 text-sm text-ink-muted" : "mt-4 grid divide-y divide-line border-t border-line text-sm", children: shown.map((f) => compact ? (_jsxs("div", { className: "truncate", children: [_jsx("dt", { className: "sr-only", children: label(f) }), _jsx("dd", { className: "truncate", children: value(row, f) })] }, f.name)) : (_jsxs("div", { className: "flex justify-between gap-4 py-2.5", children: [_jsx("dt", { className: "text-ink-muted", children: label(f) }), _jsx("dd", { className: "text-right font-medium text-ink", children: value(row, f) })] }, f.name))) }))] })] })] }, String(row.id ?? i)));
|
|
509
|
+
}) })] }));
|
|
510
|
+
}
|
|
511
|
+
// ─── Dispatch ───────────────────────────────────────────────────────────────
|
|
512
|
+
const RENDERERS = {
|
|
513
|
+
hero: Hero,
|
|
514
|
+
features: Features,
|
|
515
|
+
logos: Logos,
|
|
516
|
+
testimonials: Testimonials,
|
|
517
|
+
stats: Stats,
|
|
518
|
+
steps: Steps,
|
|
519
|
+
pricing: Pricing,
|
|
520
|
+
faq: Faq,
|
|
521
|
+
cta: Cta,
|
|
522
|
+
comparison: Comparison,
|
|
523
|
+
integrations: Integrations,
|
|
524
|
+
team: Team,
|
|
525
|
+
gallery: Gallery,
|
|
526
|
+
contact: Contact,
|
|
527
|
+
newsletter: Newsletter,
|
|
528
|
+
richText: RichText,
|
|
529
|
+
imageText: ImageText,
|
|
530
|
+
video: Video,
|
|
531
|
+
embed: Embed,
|
|
532
|
+
blogList: BlogList,
|
|
533
|
+
spacer: Spacer,
|
|
534
|
+
divider: Divider,
|
|
535
|
+
entityList: Records,
|
|
536
|
+
entityGrid: Records,
|
|
537
|
+
};
|
|
538
|
+
/** Section types the website renders (every type of SECTION_SPECS). */
|
|
539
|
+
export const SITE_SECTION_TYPES = Object.keys(RENDERERS);
|
|
540
|
+
/** One section inside a page (the page has built the translator). Hidden sections show only in previews, marked. */
|
|
541
|
+
export function renderSection(ctx) {
|
|
542
|
+
const render = RENDERERS[ctx.section.type];
|
|
543
|
+
if (!render)
|
|
544
|
+
return null;
|
|
545
|
+
if (ctx.section.hidden && !ctx.preview)
|
|
546
|
+
return null;
|
|
547
|
+
const out = render(ctx);
|
|
548
|
+
if (!ctx.section.hidden || !out)
|
|
549
|
+
return out;
|
|
550
|
+
return (_jsxs("div", { className: "forge-site-hidden relative", children: [_jsxs("span", { className: "forge-site-hidden-badge absolute top-3 right-3 z-20 inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium", children: [_jsx(EyeOff, { className: "size-3.5", "aria-hidden": true }), ctx.t("site.hiddenSection")] }), out] }));
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* One section on its own (the admin editor's live preview). Wrapped in a `@container` so its layout follows the
|
|
554
|
+
* width it is given, exactly like the page at that width.
|
|
555
|
+
*/
|
|
556
|
+
export function SiteSectionView({ app: baseApp, section, data, locale, messages, preview = false, first = false, pagePath }) {
|
|
557
|
+
const { app, lang, t } = siteTranslator(baseApp, locale, messages);
|
|
558
|
+
return _jsx("div", { className: "forge-site @container", children: renderSection({ app, section, data: data ?? section.data, lang, t, preview, first, pagePath }) });
|
|
559
|
+
}
|
|
560
|
+
//# sourceMappingURL=sections.js.map
|