Skip to content
75 linesCodeBlameRaw
1/**
2 * Schema.org JSON-LD for the pages that say what g1t is and what it costs,
3 * so search engines and AI assistants read it without parsing the prose.
4 * Each is a `script:ld+json` meta descriptor, added after `page(…)`.
5 */
6import type { MetaDescriptor } from "react-router";
7
8import { COMPANY, SOCIAL } from "./legal";
9import { DESCRIPTION, SITE } from "./meta";
10
11export function organization(): MetaDescriptor {
12 return {
13 "script:ld+json": {
14 "@context": "https://schema.org",
15 "@type": "Organization",
16 name: COMPANY.name,
17 url: COMPANY.url,
18 founder: { "@type": "Person", name: COMPANY.founder },
19 brand: { "@type": "Brand", name: "g1t", url: SITE, sameAs: SOCIAL.map((account) => account.url) },
20 },
21 };
22}
23
24/** g1t as an application, with the plan's monthly price per workspace. */
25export function application(monthlyDollars: number): MetaDescriptor {
26 return {
27 "script:ld+json": {
28 "@context": "https://schema.org",
29 "@type": "SoftwareApplication",
30 name: "g1t",
31 url: SITE,
32 applicationCategory: "DeveloperApplication",
33 operatingSystem: "Web",
34 description: DESCRIPTION,
35 publisher: { "@type": "Organization", name: COMPANY.name, url: COMPANY.url },
36 license: "https://opensource.org/licenses/MIT",
37 offers: [
38 {
39 "@type": "Offer",
40 name: "Free",
41 price: "0",
42 priceCurrency: "USD",
43 description: "Chat and the forge, with no card.",
44 },
45 {
46 "@type": "Offer",
47 name: "The g1t plan",
48 price: String(monthlyDollars),
49 priceCurrency: "USD",
50 description: "A month per workspace, never per seat, with $10 of usage included.",
51 priceSpecification: {
52 "@type": "UnitPriceSpecification",
53 price: String(monthlyDollars),
54 priceCurrency: "USD",
55 unitText: "month per workspace",
56 },
57 },
58 ],
59 },
60 };
61}
62
63export function faqPage(questions: { q: string; a: string }[]): MetaDescriptor {
64 return {
65 "script:ld+json": {
66 "@context": "https://schema.org",
67 "@type": "FAQPage",
68 mainEntity: questions.map((item) => ({
69 "@type": "Question",
70 name: item.q,
71 acceptedAnswer: { "@type": "Answer", text: item.a },
72 })),
73 },
74 };
75}