diff --git a/docs/public/robots.txt b/docs/public/robots.txt index f6021b27..a8027b6a 100644 --- a/docs/public/robots.txt +++ b/docs/public/robots.txt @@ -1,4 +1,31 @@ User-agent: * Allow: / +# AI and answer-engine crawlers are allowed to access RocketSim docs. +User-agent: GPTBot +Allow: / + +User-agent: ChatGPT-User +Allow: / + +User-agent: ClaudeBot +Allow: / + +User-agent: anthropic-ai +Allow: / + +User-agent: PerplexityBot +Allow: / + +User-agent: Googlebot +Allow: / + +User-agent: Bingbot +Allow: / + +# LLM-friendly documentation: +# https://www.rocketsim.app/llms.txt +# https://www.rocketsim.app/llms-small.txt +# https://www.rocketsim.app/llms-full.txt + Sitemap: https://www.rocketsim.app/sitemap-index.xml diff --git a/docs/src/components/starlight/Head.astro b/docs/src/components/starlight/Head.astro index 1b1d84bf..85015441 100644 --- a/docs/src/components/starlight/Head.astro +++ b/docs/src/components/starlight/Head.astro @@ -102,6 +102,13 @@ const breadcrumbSchema = + + diff --git a/docs/src/config/config.json b/docs/src/config/config.json index 2f0f0b49..60ec10a5 100644 --- a/docs/src/config/config.json +++ b/docs/src/config/config.json @@ -31,7 +31,7 @@ } ], "metadata": { - "meta_description": "Simulator Airplane Mode, Location Simulation, Accessibility Testing, Compare designs inside the iOS simulator. Test deeplinks, push notifications.", + "meta_description": "RocketSim is the iOS Simulator companion for developers and AI coding agents: monitor network requests, capture media, test accessibility, and automate app workflows.", "meta_image": "/og-banner-rocketsim.jpg" } } diff --git a/docs/src/integrations/llms-txt-post-process.ts b/docs/src/integrations/llms-txt-post-process.ts index 6652f0d7..3bf81f81 100644 --- a/docs/src/integrations/llms-txt-post-process.ts +++ b/docs/src/integrations/llms-txt-post-process.ts @@ -44,7 +44,7 @@ export function llmsTxtPostProcess(): AstroIntegration { const buildDate = new Date().toISOString().split("T")[0]; const enhancedLlmsTxt = `# RocketSim -> RocketSim is a macOS developer tool that enhances Apple's iOS Simulator with professional-grade features for capturing, debugging, testing, and design validation. Available on the Mac App Store for iOS, macOS, watchOS, and visionOS developers. +> RocketSim is a macOS developer tool and iOS Simulator companion for app developers and AI coding agents. It adds professional capture workflows, URLSession network monitoring without proxy certificates, Simulator app actions, accessibility testing, and a version-matched CLI plus Agent Skill for tools like Cursor, Claude, Codex, and Xcode. Last updated: ${buildDate} Website: https://www.rocketsim.app @@ -55,13 +55,24 @@ Support: support@rocketsim.app ## Key Features - Professional screenshot and video capture with device bezels -- Network traffic monitoring and debugging +- URLSession network monitoring without proxy setup or custom certificates +- AI-ready network request prompt exports for Claude, ChatGPT, and other assistants +- RocketSim CLI and Agent Skill for AI coding agents - Push notification and deep link testing - Design comparison with pixel-perfect overlays - Accessibility testing (Dynamic Type, VoiceOver Navigator) - Xcode build insights and team analytics - Network speed throttling and Simulator airplane mode +## Canonical Feature Docs + +- [Network Traffic Monitoring](https://www.rocketsim.app/docs/features/networking/network-traffic-monitoring): inspect URLSession requests, responses, headers, logs, metrics, cURL commands, and AI-ready exports without proxy certificates +- [Networking Insights](https://www.rocketsim.app/docs/features/networking/networking-insights): analyze duplicate calls, caching opportunities, slow endpoints, failure spikes, and most requested URLs across sessions +- [AI Network Request Prompts](https://www.rocketsim.app/docs/features/networking/network-request-prompts): export redacted request summaries and built-in debugging prompts for AI assistants +- [Agentic Development with RocketSim](https://www.rocketsim.app/docs/features/agentic-development/): let AI coding agents inspect, navigate, and verify running iOS Simulator apps +- [RocketSim CLI](https://www.rocketsim.app/docs/features/agentic-development/rocketsim-cli): use the built-in CLI for visible elements, semantic interactions, waits, screenshots, and recordings +- [RocketSim Agent Skill](https://www.rocketsim.app/docs/features/agentic-development/agent-skill): install version-matched instructions for AI coding tools + ## Documentation Sets - [Abridged documentation](https://www.rocketsim.app/llms-small.txt): compact version with non-essential content removed diff --git a/docs/src/layouts/components/SEO.astro b/docs/src/layouts/components/SEO.astro index 79f38d9b..3bf2afa2 100644 --- a/docs/src/layouts/components/SEO.astro +++ b/docs/src/layouts/components/SEO.astro @@ -117,6 +117,13 @@ const ogType = article ? "article" : "website"; + + {robotsContent && } diff --git a/docs/src/layouts/components/StructuredData.astro b/docs/src/layouts/components/StructuredData.astro index 0e1acd9b..e117098b 100644 --- a/docs/src/layouts/components/StructuredData.astro +++ b/docs/src/layouts/components/StructuredData.astro @@ -7,6 +7,20 @@ import { normalizeCanonicalUrl, toAbsoluteSecureUrl } from "@/lib/utils/seo"; // structured data matches the canonical form emitted in meta tags. const SITE_ROOT_URL = normalizeCanonicalUrl("/"); +type FAQItem = { + question: string; + answer: string; +}; + +type ItemListEntry = { + name: string; + url: string; + description?: string; + dateModified?: string; +}; + +type WebPageType = "WebPage" | "CollectionPage"; + type ArticleProps = { type: "article"; article: { @@ -33,6 +47,7 @@ type ProductProps = { description: string | undefined; image?: string; url: string; + featureList?: string[]; }; }; @@ -47,14 +62,31 @@ type OfferProps = { type StaticProps = { type: "static"; + page?: { + name: string; + description?: string; + url: string; + pageType?: WebPageType; + breadcrumbName?: string; + about?: string[]; + itemList?: ItemListEntry[]; + }; + software?: { + description?: string; + featureList?: string[]; + }; }; type WebsiteProps = { type: "website"; }; +type FAQProps = { + faq?: FAQItem[]; +}; + // prettier-ignore -export type Props = ArticleProps | ProductProps | OfferProps | StaticProps | WebsiteProps; +export type Props = (ArticleProps | ProductProps | OfferProps | StaticProps | WebsiteProps) & FAQProps; // TypeScript interfaces for JSON-LD schemas interface SchemaBase { @@ -93,8 +125,11 @@ interface OrganizationSchema extends SchemaBase { type SchemaRef = { "@id": string }; interface ArticleSchema extends SchemaBase { - "@type": "Article"; + "@type": "Article" | "BlogPosting"; + "@id"?: string; headline: string; + description?: string; + url?: string; datePublished: string; dateModified: string; author: PersonSchema | SchemaRef; @@ -108,11 +143,18 @@ interface ArticleSchema extends SchemaBase { } interface WebPageSchema extends SchemaBase { - "@type": "WebPage"; + "@type": WebPageType; + "@id"?: string; name: string; url: string; + description?: string; datePublished?: string; dateModified?: string; + isPartOf?: SchemaRef; + about?: Array<{ + "@type": "Thing"; + name: string; + }>; breadcrumb: { "@id": string; }; @@ -142,13 +184,17 @@ interface WebSiteSchema extends SchemaBase { interface SoftwareApplicationSchema extends SchemaBase { "@type": "SoftwareApplication"; + "@id"?: string; name: string; + url?: string; applicationCategory: string; operatingSystem: string; description?: string; owner: OrganizationSchema | SchemaRef; offers?: OfferSchema | OfferSchema[]; image?: string; + featureList?: string[]; + sameAs?: string[]; } interface OfferSchema extends SchemaBase { @@ -169,6 +215,31 @@ interface OfferSchema extends SchemaBase { description?: string; } +interface ItemListSchema extends SchemaBase { + "@type": "ItemList"; + "@id": string; + itemListElement: Array<{ + "@type": "ListItem"; + position: number; + url: string; + name: string; + description?: string; + dateModified?: string; + }>; +} + +interface FAQPageSchema extends SchemaBase { + "@type": "FAQPage"; + mainEntity: Array<{ + "@type": "Question"; + name: string; + acceptedAnswer: { + "@type": "Answer"; + text: string; + }; + }>; +} + const { type } = Astro.props; // Stable identifiers for the canonical entity nodes. Using URL fragments keeps @@ -177,6 +248,18 @@ const { type } = Astro.props; const ORGANIZATION_ID = `${config.site.base_url}/#organization`; const AUTHOR_ID = `${config.site.base_url}/#person-twannl`; const WEBSITE_ID = `${config.site.base_url}/#website`; +const SOFTWARE_ID = `${config.site.base_url}/#software`; + +const softwareFeatureList = [ + "iOS Simulator screenshots and video recordings", + "URLSession network monitoring without proxy certificates", + "AI-ready network request prompt exports", + "RocketSim CLI and Agent Skill for AI coding agents", + "Simulator camera, location, permissions, deeplinks, and push notification testing", + "Accessibility testing and VoiceOver workflows", + "Network speed control and Simulator airplane mode", + "Xcode build insights", +]; // Author data (hardcoded - single author blog) const authorData: PersonSchema = { @@ -211,6 +294,71 @@ const organizationData: OrganizationSchema = { ], }; +function createSoftwareApplicationSchema({ + description, + image, + featureList, + offers, +}: { + description?: string; + image?: string; + featureList?: string[]; + offers?: OfferSchema[]; +}): SoftwareApplicationSchema { + return { + "@context": "https://schema.org", + "@type": "SoftwareApplication", + "@id": SOFTWARE_ID, + name: "RocketSim", + url: SITE_ROOT_URL, + applicationCategory: "DeveloperApplication", + operatingSystem: "macOS", + description: + description ?? + "RocketSim is an iOS Simulator companion for developers and AI coding agents.", + owner: { "@id": ORGANIZATION_ID }, + image: + toAbsoluteSecureUrl(image ?? "/images/rocketsim-app-icon.png") ?? + `${config.site.base_url}/images/rocketsim-app-icon.png`, + featureList: featureList ?? softwareFeatureList, + sameAs: [ + "https://apps.apple.com/app/apple-store/id1504940162", + "https://github.com/AvdLee/RocketSimApp", + "https://x.com/rocketsim_app", + "https://www.youtube.com/@rocketsimapp", + ], + ...(offers && { offers }), + }; +} + +function createBreadcrumbSchema({ + url, + name, +}: { + url: string; + name: string; +}): BreadcrumbListSchema { + return { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + "@id": `${url}#breadcrumb`, + itemListElement: [ + { + "@type": "ListItem", + position: 1, + name: "Home", + item: SITE_ROOT_URL, + }, + { + "@type": "ListItem", + position: 2, + name, + item: url, + }, + ], + }; +} + // Build schemas based on type const schemas: SchemaBase[] = []; @@ -228,8 +376,11 @@ if (type === "article") { // 1. Article schema (references Organization/Person via @id, no inline copies) const articleSchema: ArticleSchema = { "@context": "https://schema.org", - "@type": "Article", + "@type": "BlogPosting", + "@id": `${articleUrl}#article`, headline: article.headline, + description: article.description, + url: articleUrl, datePublished: article.datePublished, dateModified: article.dateModified, author: { "@id": AUTHOR_ID }, @@ -247,6 +398,7 @@ if (type === "article") { url: articleImageUrl, ...(article.image.width && { width: article.image.width }), ...(article.image.height && { height: article.image.height }), + ...(article.image.caption && { caption: article.image.caption }), } as ImageObject, }), }; @@ -306,6 +458,8 @@ if (type === "article") { } if (type === "static") { + const { page, software } = Astro.props; + // Static schemas for WebSite (Organization is emitted globally above). const webSiteSchema: WebSiteSchema = { "@context": "https://schema.org", @@ -319,6 +473,54 @@ if (type === "static") { }; schemas.push(webSiteSchema); + + if (software) { + schemas.push(createSoftwareApplicationSchema(software)); + } + + if (page) { + const pageUrl = normalizeCanonicalUrl(page.url); + const webPageSchema: WebPageSchema = { + "@context": "https://schema.org", + "@type": page.pageType ?? "WebPage", + "@id": pageUrl, + name: page.name, + url: pageUrl, + ...(page.description && { description: page.description }), + isPartOf: { "@id": WEBSITE_ID }, + breadcrumb: { "@id": `${pageUrl}#breadcrumb` }, + ...(page.about && { + about: page.about.map((name) => ({ + "@type": "Thing" as const, + name, + })), + }), + }; + schemas.push(webPageSchema); + schemas.push( + createBreadcrumbSchema({ + url: pageUrl, + name: page.breadcrumbName ?? page.name, + }), + ); + + if (page.itemList?.length) { + const itemListSchema: ItemListSchema = { + "@context": "https://schema.org", + "@type": "ItemList", + "@id": `${pageUrl}#item-list`, + itemListElement: page.itemList.map((item, index) => ({ + "@type": "ListItem" as const, + position: index + 1, + name: item.name, + url: normalizeCanonicalUrl(item.url), + ...(item.description && { description: item.description }), + ...(item.dateModified && { dateModified: item.dateModified }), + })), + }; + schemas.push(itemListSchema); + } + } } if (type === "product") { @@ -328,14 +530,13 @@ if (type === "product") { // 1. SoftwareApplication schema (references Organization via @id). const softwareSchema: SoftwareApplicationSchema = { - "@context": "https://schema.org", - "@type": "SoftwareApplication", + ...createSoftwareApplicationSchema({ + description: product.description, + image: productImageUrl, + featureList: product.featureList, + }), name: product.name, - applicationCategory: "DeveloperApplication", - operatingSystem: "macOS", - ...(product.description && { description: product.description }), - owner: { "@id": ORGANIZATION_ID }, - ...(productImageUrl && { image: productImageUrl }), + url: productUrl, }; schemas.push(softwareSchema); @@ -350,26 +551,7 @@ if (type === "product") { schemas.push(webPageSchema); // 3. BreadcrumbList schema - const breadcrumbSchema: BreadcrumbListSchema = { - "@context": "https://schema.org", - "@type": "BreadcrumbList", - "@id": `${productUrl}#breadcrumb`, - itemListElement: [ - { - "@type": "ListItem", - position: 1, - name: "Home", - item: SITE_ROOT_URL, - }, - { - "@type": "ListItem", - position: 2, - name: product.name, - item: productUrl, - }, - ], - }; - schemas.push(breadcrumbSchema); + schemas.push(createBreadcrumbSchema({ url: productUrl, name: product.name })); } if (type === "offer") { @@ -417,19 +599,10 @@ if (type === "offer") { }, ]; - const softwareWithOffersSchema: SoftwareApplicationSchema = { - "@context": "https://schema.org", - "@type": "SoftwareApplication", - name: "RocketSim", - applicationCategory: "DeveloperApplication", - operatingSystem: "macOS", - ...(offers.description && { description: offers.description }), - owner: { "@id": ORGANIZATION_ID }, - image: - toAbsoluteSecureUrl("/images/rocketsim-app-icon.png") ?? - `${config.site.base_url}/images/rocketsim-app-icon.png`, + const softwareWithOffersSchema = createSoftwareApplicationSchema({ + description: offers.description, offers: pricingOffers, - }; + }); schemas.push(softwareWithOffersSchema); // 2. WebPage schema @@ -443,26 +616,26 @@ if (type === "offer") { schemas.push(webPageSchema); // 3. BreadcrumbList schema - const breadcrumbSchema: BreadcrumbListSchema = { + schemas.push(createBreadcrumbSchema({ url: offerUrl, name: offers.title })); +} + +const faqItems = Astro.props.faq?.filter( + (item) => item.question.trim() && item.answer.trim(), +); +if (faqItems?.length) { + const faqSchema: FAQPageSchema = { "@context": "https://schema.org", - "@type": "BreadcrumbList", - "@id": `${offerUrl}#breadcrumb`, - itemListElement: [ - { - "@type": "ListItem", - position: 1, - name: "Home", - item: SITE_ROOT_URL, + "@type": "FAQPage", + mainEntity: faqItems.map((item) => ({ + "@type": "Question", + name: item.question, + acceptedAnswer: { + "@type": "Answer", + text: item.answer, }, - { - "@type": "ListItem", - position: 2, - name: offers.title, - item: offerUrl, - }, - ], + })), }; - schemas.push(breadcrumbSchema); + schemas.push(faqSchema); } const structuredDataGraph = { diff --git a/docs/src/pages/blog/[slug].astro b/docs/src/pages/blog/[slug].astro index 7b4821f9..c5c02908 100644 --- a/docs/src/pages/blog/[slug].astro +++ b/docs/src/pages/blog/[slug].astro @@ -51,6 +51,7 @@ const imageType = image const canonicalUrl = `${base_url}/blog/${post.id}`; const ogImage = image ? `${base_url}${image.src}` : undefined; const appStoreHref = `https://apps.apple.com/app/apple-store/id1504940162?pt=117264678&ct=blog-${post.id}-sticky-badge&mt=8`; +const wordCount = (post.body ?? "").trim().split(/\s+/).filter(Boolean).length; const seo = { title: `${title} - RocketSim`, @@ -66,6 +67,7 @@ const seo = { article: { publishedTime: publishedTime.toISOString(), modifiedTime: modifiedTime.toISOString(), + wordCount, }, }; @@ -83,8 +85,10 @@ const structuredData = { url: ogImage, width: image?.width, height: image?.height, + caption: imageCaption, } : undefined, + wordCount, }, }; --- diff --git a/docs/src/pages/blog/index.astro b/docs/src/pages/blog/index.astro index ad741ae1..d0897240 100644 --- a/docs/src/pages/blog/index.astro +++ b/docs/src/pages/blog/index.astro @@ -32,6 +32,25 @@ const seo = { const structuredData = { type: "static" as const, + page: { + pageType: "CollectionPage" as const, + name: "RocketSim Blog", + description: seo.description, + url: seo.canonical, + breadcrumbName: "Blog", + about: [ + "iOS Simulator development", + "RocketSim", + "URLSession debugging", + "AI coding agents", + ], + itemList: items.map((post) => ({ + name: post.title, + description: post.description, + url: `${base_url}/blog/${post.slug}`, + dateModified: post.date.toISOString(), + })), + }, }; --- diff --git a/docs/src/pages/features/[featurePageSlug].astro b/docs/src/pages/features/[featurePageSlug].astro index c66c4944..aad5b070 100644 --- a/docs/src/pages/features/[featurePageSlug].astro +++ b/docs/src/pages/features/[featurePageSlug].astro @@ -23,15 +23,32 @@ const { title, description, hero, bento } = page.data; const { site: { base_url }, } = config; +const canonicalUrl = `${base_url}/features/${page.id}`; +const featureItems = await getCollection( + "feature", + (feature) => feature.data.featurePage === page.id, +); const seo = { title: page.data.meta_title || `${title} - RocketSim`, description, - canonical: `${base_url}/features/${page.id}`, + canonical: canonicalUrl, }; const structuredData = { type: "static" as const, + page: { + name: title, + description, + url: canonicalUrl, + breadcrumbName: title, + about: [title, "RocketSim", "iOS Simulator"], + itemList: featureItems.map((feature) => ({ + name: feature.data.name, + description: feature.data.tagLine, + url: feature.data.docPath ?? canonicalUrl, + })), + }, }; --- diff --git a/docs/src/pages/index.astro b/docs/src/pages/index.astro index fdf1935f..85b59b63 100644 --- a/docs/src/pages/index.astro +++ b/docs/src/pages/index.astro @@ -33,9 +33,29 @@ const seo = { description: meta_description, canonical: base_url, }; + +const structuredData = { + type: "static" as const, + software: { + description: + "RocketSim is the iOS Simulator companion for developers and AI coding agents, with tools for network monitoring, captures, app actions, accessibility testing, and Simulator automation.", + }, + page: { + name: "RocketSim", + description: meta_description, + url: base_url, + about: [ + "iOS Simulator", + "URLSession network monitoring", + "AI coding agents", + "Simulator screenshots and recordings", + "iOS app testing", + ], + }, +}; --- - +
({ + question: item.question, + answer: item.answer, + })), }; ---