Skip to content

Commit 6f1bf1a

Browse files
committed
Add JSON-LD structured data to docs pages
Inject TechArticle and BreadcrumbList schemas on all docs pages. Add FAQPage schema on the FAQ page for rich search results.
1 parent b0424f5 commit 6f1bf1a

File tree

1 file changed

+149
-4
lines changed

1 file changed

+149
-4
lines changed

docs/src/components/starlight/Head.astro

Lines changed: 149 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,145 @@
11
---
2-
/**
3-
* Custom Head component for RocketSim docs
4-
* Includes proper favicon and meta tags matching the main site
5-
*/
62
import Default from "@astrojs/starlight/components/Head.astro";
73
import PlausibleAnalytics from "@/components/PlausibleAnalytics.astro";
4+
5+
const baseUrl = "https://www.rocketsim.app";
6+
const { entry } = Astro.props;
7+
const title = entry?.data?.title ?? "RocketSim Docs";
8+
const description = entry?.data?.description ?? "";
9+
const slug = entry?.slug ?? "";
10+
const pageUrl = `${baseUrl}/${slug}`;
11+
12+
const pathSegments = slug.split("/").filter(Boolean);
13+
const breadcrumbItems = [
14+
{ name: "Home", url: baseUrl },
15+
{ name: "Docs", url: `${baseUrl}/docs` },
16+
];
17+
let currentPath = "";
18+
for (const segment of pathSegments.slice(1)) {
19+
currentPath += `/${segment}`;
20+
const name = segment
21+
.split("-")
22+
.map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
23+
.join(" ");
24+
breadcrumbItems.push({ name, url: `${baseUrl}/docs${currentPath}` });
25+
}
26+
if (breadcrumbItems.length > 2) {
27+
breadcrumbItems[breadcrumbItems.length - 1].name = title;
28+
}
29+
30+
const isFaqPage = slug.endsWith("support/faq");
31+
32+
const schemas: Record<string, unknown>[] = [];
33+
34+
if (title !== "RocketSim Docs" && title !== "404") {
35+
schemas.push({
36+
"@context": "https://schema.org",
37+
"@type": "TechArticle",
38+
headline: title,
39+
description,
40+
url: pageUrl,
41+
author: {
42+
"@type": "Organization",
43+
name: "RocketSim",
44+
url: baseUrl,
45+
},
46+
publisher: {
47+
"@type": "Organization",
48+
name: "RocketSim",
49+
url: baseUrl,
50+
logo: `${baseUrl}/images/rocketsim-app-icon.png`,
51+
},
52+
mainEntityOfPage: {
53+
"@type": "WebPage",
54+
"@id": pageUrl,
55+
},
56+
});
57+
}
58+
59+
if (breadcrumbItems.length > 1) {
60+
schemas.push({
61+
"@context": "https://schema.org",
62+
"@type": "BreadcrumbList",
63+
itemListElement: breadcrumbItems.map((item, index) => ({
64+
"@type": "ListItem",
65+
position: index + 1,
66+
name: item.name,
67+
item: item.url,
68+
})),
69+
});
70+
}
71+
72+
const faqEntries = isFaqPage
73+
? [
74+
{
75+
q: "Does RocketSim offer out-of-the App Store distribution?",
76+
a: "Yes. Get in touch via support@rocketsim.app.",
77+
},
78+
{
79+
q: "Are there non-recurring subscriptions?",
80+
a: "Yes, you can consider buying Team Licenses at rocketsim.app/team-insights.",
81+
},
82+
{
83+
q: "Can I reimburse my App Store subscription?",
84+
a: "Yes, RocketSim offers Team Licenses as an alternative at rocketsim.app/team-insights.",
85+
},
86+
{
87+
q: "Do you provide commercial or team licenses?",
88+
a: "Yes, check out Team Licenses at rocketsim.app/team-insights.",
89+
},
90+
{
91+
q: "Can I buy a lifetime RocketSim license?",
92+
a: "No, but you can earn a lifetime license through SwiftLee Weekly's referral program. Join the newsletter and follow the instructions.",
93+
},
94+
{
95+
q: "Why is my video not accepted by App Store Connect?",
96+
a: "Videos are optimized for App Previews following Apple's specifications. Make sure to use a Simulator matching a device from the App Preview specifications.",
97+
},
98+
{
99+
q: "Why wouldn't I just use xcrun simctl?",
100+
a: "RocketSim uses xcrun simctl under the hood but enhances the output with touches, device bezels, and a visual interface for quicker access.",
101+
},
102+
{
103+
q: "Why does RocketSim need screen recording permissions?",
104+
a: "RocketSim is sandboxed and needs screen recording permissions to read Simulator window titles, which it uses to determine the currently active Simulator.",
105+
},
106+
{
107+
q: "Where can I report bugs or feature requests?",
108+
a: "Issues and feature requests are managed on GitHub at github.com/AvdLee/RocketSimApp/issues.",
109+
},
110+
{
111+
q: "How can I get PNG images instead of JPEG?",
112+
a: "Disable App Store Connect (ASC) Optimization. ASC requires JPEG images without alpha layer.",
113+
},
114+
{
115+
q: "Why are my iPad captures upside-down?",
116+
a: "RocketSim cannot detect landscape-left or landscape-right and defaults to one rotation. Rotate your Simulator twice and restart the recording.",
117+
},
118+
{
119+
q: "Can I create transparent captures?",
120+
a: "Yes, disable App Preview Optimized and set your background color to transparent.",
121+
},
122+
{
123+
q: "Network Speed Control isn't working, what can I do?",
124+
a: "Quit Xcode, all Simulators, and RocketSim, then reopen them. Check System Settings → Privacy & Security for pending permissions. On macOS Sequoia, enable the Network Extension in System Settings → General → Login Items & Extensions.",
125+
},
126+
]
127+
: [];
128+
129+
if (isFaqPage && faqEntries.length > 0) {
130+
schemas.push({
131+
"@context": "https://schema.org",
132+
"@type": "FAQPage",
133+
mainEntity: faqEntries.map((faq) => ({
134+
"@type": "Question",
135+
name: faq.q,
136+
acceptedAnswer: {
137+
"@type": "Answer",
138+
text: faq.a,
139+
},
140+
})),
141+
});
142+
}
8143
---
9144

10145
<Default {...Astro.props}><slot /></Default>
@@ -22,4 +157,14 @@ import PlausibleAnalytics from "@/components/PlausibleAnalytics.astro";
22157
<!-- Additional meta for better SEO -->
23158
<meta name="author" content="RocketSim" />
24159

160+
{
161+
schemas.map((schema) => (
162+
<script
163+
type="application/ld+json"
164+
is:inline
165+
set:html={JSON.stringify(schema)}
166+
/>
167+
))
168+
}
169+
25170
<PlausibleAnalytics />

0 commit comments

Comments
 (0)