diff --git a/docs/astro.config.ts b/docs/astro.config.ts index 2a446f01..67877f66 100644 --- a/docs/astro.config.ts +++ b/docs/astro.config.ts @@ -11,6 +11,7 @@ import sitemap from "@astrojs/sitemap"; import { llmsTxtPostProcess } from "./src/integrations/llms-txt-post-process"; import config from "./src/config/config.json"; +import { sidebarGroups } from "./src/config/sidebar-groups"; // https://astro.build/config export default defineConfig({ @@ -120,72 +121,24 @@ export default defineConfig({ Footer: "./src/components/starlight/Footer.astro", SiteTitle: "./src/components/starlight/SiteTitle.astro", }, - sidebar: [ - { - label: "Getting Started", - collapsed: true, - autogenerate: { directory: "docs/getting-started" }, - }, - { - label: "Screenshots & Recordings", - collapsed: true, - items: [ - { slug: "docs/features/capturing/screenshots" }, - { slug: "docs/features/capturing/recordings" }, - { slug: "docs/features/capturing/floating-thumbnail" }, - { slug: "docs/features/capturing/app-store-connect-optimization" }, - { slug: "docs/features/capturing/touch-indicators" }, - { slug: "docs/features/capturing/120-fps-recordings" }, - ], - }, - { - label: "Simulator Camera", - link: "/docs/features/capturing/simulator-camera-support", - }, - { - label: "Status Bar", - link: "/docs/features/capturing/statusbar-appearance", - }, - { - label: "Design Comparison", - collapsed: true, - autogenerate: { directory: "docs/features/design-comparison" }, - }, - { - label: "App Actions", - collapsed: true, - autogenerate: { directory: "docs/features/app-actions" }, - }, - { - label: "Networking", - collapsed: true, - autogenerate: { directory: "docs/features/networking" }, - }, - { - label: "Build Insights", - collapsed: true, - autogenerate: { directory: "docs/features/build-insights" }, - }, - { - label: "Accessibility", - collapsed: true, - autogenerate: { directory: "docs/features/accessibility" }, - }, - { - label: "User Defaults Editor", - link: "/docs/features/user-defaults-editor", - }, - { - label: "Settings", - collapsed: true, - autogenerate: { directory: "docs/settings" }, - }, - { - label: "Support", - collapsed: true, - autogenerate: { directory: "docs/support" }, - }, - ], + sidebar: sidebarGroups.map((group) => { + if (group.directory) { + return { + label: group.label, + collapsed: group.collapsed, + autogenerate: { directory: group.directory }, + }; + } + const slugs = group.slugs!; + if (slugs.length === 1) { + return { label: group.label, link: `/${slugs[0]}` }; + } + return { + label: group.label, + collapsed: group.collapsed, + items: slugs.map((slug) => ({ slug })), + }; + }), }), mdx(), llmsTxtPostProcess(), diff --git a/docs/public/llms-ctx.txt b/docs/public/llms-ctx.txt new file mode 100644 index 00000000..113eb5d7 --- /dev/null +++ b/docs/public/llms-ctx.txt @@ -0,0 +1,43 @@ +# RocketSim — Product Context + +## What is RocketSim? +RocketSim is a macOS developer tool that enhances Apple's iOS Simulator with professional-grade features for capturing, debugging, testing, and design validation. It's available on the Mac App Store and used by individual developers and large teams worldwide. + +## Who is it for? +iOS, macOS, watchOS, and visionOS developers who use Xcode and the iOS Simulator for app development and testing. + +## Core Feature Areas + +### Capturing +Professional screenshots and recordings with device bezels, custom backgrounds, touch indicators, and App Store Connect optimization. Supports GIF, MP4, and JPEG/PNG formats. + +### Networking +Real-time network traffic monitoring via RocketSim Connect, historical networking insights, and network speed throttling (3G, Edge, airplane mode) for testing poor connectivity. + +### App Actions +Test push notifications, deep links/universal links, location simulation, and privacy permissions without a physical device. Manage everything from a persistent side window. + +### Design Comparison +Overlay design mockups from Figma or Sketch with adjustable opacity. Add grid overlays and rulers. Use the pixel-level magnifier for precise layout verification. + +### Accessibility +Toggle accessibility settings (Dynamic Type, Increase Contrast, Bold Text, Reduce Motion, etc.) from the side window. VoiceOver Navigator visualizes element order and supports keyboard-driven navigation. + +### Build Insights +Track Xcode build counts, durations, and trends. Compare incremental vs. clean builds. Team Build Insights lets teams compare performance across machines and Xcode versions. + +### Other +User Defaults real-time editor, status bar customization, simulator camera support, and configurable keyboard shortcuts. + +## Technical Details +- Platform: macOS (enhances the iOS/watchOS/visionOS Simulator in Xcode) +- Distribution: Mac App Store (out-of-store distribution available on request — contact support@rocketsim.app) +- Sandboxed: Yes +- RocketSim Connect: Local Bonjour-based framework for Simulator-to-RocketSim communication (debug builds only) + +## Links +- Website: https://www.rocketsim.app +- Documentation: https://www.rocketsim.app/docs +- GitHub (issues & features): https://github.com/AvdLee/RocketSimApp +- YouTube: https://www.youtube.com/@rocketsimapp +- Developer: Antoine van der Lee (https://www.avanderlee.com) diff --git a/docs/src/config/sidebar-groups.ts b/docs/src/config/sidebar-groups.ts new file mode 100644 index 00000000..d82a4aed --- /dev/null +++ b/docs/src/config/sidebar-groups.ts @@ -0,0 +1,75 @@ +interface SidebarGroupDef { + label: string; + collapsed?: boolean; + /** For autogenerate groups: directory relative to the content collection root */ + directory?: string; + /** For explicit item/link groups: slug paths relative to the content collection root */ + slugs?: string[]; +} + +export const sidebarGroups: SidebarGroupDef[] = [ + { + label: "Getting Started", + collapsed: true, + directory: "docs/getting-started", + }, + { + label: "Screenshots & Recordings", + collapsed: true, + slugs: [ + "docs/features/capturing/screenshots", + "docs/features/capturing/recordings", + "docs/features/capturing/floating-thumbnail", + "docs/features/capturing/app-store-connect-optimization", + "docs/features/capturing/touch-indicators", + "docs/features/capturing/120-fps-recordings", + ], + }, + { + label: "Simulator Camera", + slugs: ["docs/features/capturing/simulator-camera-support"], + }, + { + label: "Status Bar", + slugs: ["docs/features/capturing/statusbar-appearance"], + }, + { + label: "Design Comparison", + collapsed: true, + directory: "docs/features/design-comparison", + }, + { + label: "App Actions", + collapsed: true, + directory: "docs/features/app-actions", + }, + { + label: "Networking", + collapsed: true, + directory: "docs/features/networking", + }, + { + label: "Build Insights", + collapsed: true, + directory: "docs/features/build-insights", + }, + { + label: "Accessibility", + collapsed: true, + directory: "docs/features/accessibility", + }, + { + label: "User Defaults Editor", + slugs: ["docs/features/user-defaults-editor"], + }, + { + label: "Settings", + collapsed: true, + directory: "docs/settings", + }, + { + label: "Support", + collapsed: true, + directory: "docs/support", + }, +]; diff --git a/docs/src/integrations/llms-txt-post-process.ts b/docs/src/integrations/llms-txt-post-process.ts index 6652f0d7..b340baaa 100644 --- a/docs/src/integrations/llms-txt-post-process.ts +++ b/docs/src/integrations/llms-txt-post-process.ts @@ -1,10 +1,58 @@ import type { AstroIntegration } from "astro"; -import { readFile, writeFile } from "node:fs/promises"; +import { readFile, readdir, writeFile } from "node:fs/promises"; import { fileURLToPath } from "node:url"; import path from "node:path"; +import { sidebarGroups } from "../config/sidebar-groups"; +import siteConfig from "../config/config.json"; + +const baseUrl = siteConfig.site.base_url; + +interface PageInfo { + title: string; + slug: string; + order: number; + groupLabel: string; +} + +const FULL_SYSTEM_HEADER = ` +This is the official documentation for RocketSim, a macOS developer tool that enhances Apple's iOS Simulator. + +Product: RocketSim — available on the Mac App Store +Developer: Antoine van der Lee (https://www.avanderlee.com) +Website: ${baseUrl} +Supported platforms: iOS, macOS, watchOS, and visionOS development via the Simulator + +Key capabilities: +- Professional screenshot and video capture with device bezels and App Store Connect optimization +- Network traffic monitoring and debugging via RocketSim Connect +- Push notification testing without a physical device +- Deep link and universal link testing +- Location simulation with custom coordinates and predefined routes +- Design comparison with pixel-perfect overlays, grids, and rulers +- Accessibility testing: Dynamic Type, environment overrides, and VoiceOver Navigator +- Xcode build insights and team build analytics +- User Defaults real-time editor +- Network speed throttling and Simulator airplane mode +- Privacy permission management (grant, revoke, reset) + +Target audience: iOS, macOS, watchOS, and visionOS developers using Xcode and the Simulator. +`; + +const SMALL_SYSTEM_HEADER = ` +This is a compact version of the official documentation for RocketSim, a macOS developer tool that enhances Apple's iOS Simulator with features for capturing, networking, design comparison, accessibility testing, and more. + +Product: RocketSim — available on the Mac App Store +Developer: Antoine van der Lee (https://www.avanderlee.com) +Website: ${baseUrl} +Supported platforms: iOS, macOS, watchOS, and visionOS development via the Simulator +Target audience: iOS, macOS, watchOS, and visionOS developers using Xcode and the Simulator. +`; /** * Post-processes the generated llms-full.txt and llms-small.txt files to: + * - Replace the SYSTEM header with an enriched product context block + * - Inject an auto-generated table of contents derived from the sidebar config + * - Add cross-reference "Related" links between pages in the same sidebar group * - Remove the home/index page (only contains navigation components) * - Remove the 404 page * - Convert components to YouTube links @@ -14,6 +62,8 @@ import path from "node:path"; * - Convert :::tip/:::note directives to blockquotes (full) or strip them (small) * - Fix escaped bold-in-link markdown as safety net * - Differentiate llms-small.txt (strip testimonials, tips, extra whitespace) + * + * Also enhances llms.txt with a reference to llms-ctx.txt. */ export function llmsTxtPostProcess(): AstroIntegration { return { @@ -21,6 +71,8 @@ export function llmsTxtPostProcess(): AstroIntegration { hooks: { "astro:build:done": async ({ dir }) => { const outputDir = fileURLToPath(dir); + const contentRoot = path.join(process.cwd(), "src/content/docs"); + const pages = await scanContentPages(contentRoot); const variants = [ { file: "llms-full.txt", variant: "full" as const }, @@ -31,24 +83,190 @@ export function llmsTxtPostProcess(): AstroIntegration { const filePath = path.join(outputDir, file); try { let content = await readFile(filePath, "utf-8"); - content = transformLlmsTxt(content, variant); + const toc = buildTableOfContents(pages, variant); + const relatedLinks = buildRelatedLinks(pages, variant); + content = transformLlmsTxt(content, variant, toc, relatedLinks); await writeFile(filePath, content, "utf-8"); } catch { // File might not exist, skip } } - // Enhance llms.txt with richer metadata - const llmsTxtPath = path.join(outputDir, "llms.txt"); - try { - const buildDate = new Date().toISOString().split("T")[0]; - const enhancedLlmsTxt = `# RocketSim + await enhanceLlmsTxtIndex(outputDir); + }, + }, + }; +} + +async function scanContentPages(contentRoot: string): Promise { + const pages: PageInfo[] = []; + + for (const group of sidebarGroups) { + if (group.directory) { + const dirPath = path.join(contentRoot, group.directory); + let files: string[]; + try { + files = await readdir(dirPath); + } catch { + continue; + } + + for (const file of files.filter( + (f) => f.endsWith(".md") || f.endsWith(".mdx"), + )) { + const content = await readFile(path.join(dirPath, file), "utf-8"); + const fm = extractFrontmatter(content); + const slug = group.directory + "/" + file.replace(/\.(mdx?)$/, ""); + pages.push({ + title: fm.title, + slug, + order: fm.sidebarOrder ?? 999, + groupLabel: group.label, + }); + } + } else if (group.slugs) { + for (let i = 0; i < group.slugs.length; i++) { + const slug = group.slugs[i]; + const content = await readContentFile(contentRoot, slug); + if (!content) continue; + const fm = extractFrontmatter(content); + pages.push({ + title: fm.title, + slug, + order: i, + groupLabel: group.label, + }); + } + } + } + + return pages; +} + +async function readContentFile( + contentRoot: string, + slug: string, +): Promise { + for (const ext of [".md", ".mdx"]) { + try { + return await readFile(path.join(contentRoot, slug + ext), "utf-8"); + } catch { + continue; + } + } + return null; +} + +function extractFrontmatter(content: string): { + title: string; + sidebarOrder?: number; +} { + const fmMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (!fmMatch) return { title: "" }; + const fm = fmMatch[1]; + const titleMatch = fm.match(/title:\s*"([^"]+)"/); + const orderMatch = fm.match(/order:\s*(\d+)/); + return { + title: titleMatch?.[1] ?? "", + sidebarOrder: orderMatch ? parseInt(orderMatch[1]) : undefined, + }; +} + +function buildTableOfContents( + pages: PageInfo[], + variant: "full" | "small", +): string { + const groupOrder = sidebarGroups.map((g) => g.label); + const grouped = new Map(); + + for (const label of groupOrder) { + grouped.set(label, []); + } + + for (const page of pages) { + if (variant === "small" && page.title === "Testimonials") continue; + grouped.get(page.groupLabel)?.push(page); + } + + let toc = "## Table of Contents\n"; + + for (const label of groupOrder) { + const groupPages = grouped.get(label); + if (!groupPages || groupPages.length === 0) continue; + + groupPages.sort((a, b) => a.order - b.order); + + toc += `\n### ${label}\n`; + for (const page of groupPages) { + toc += `- ${page.title}\n`; + } + } + + return toc.trimEnd(); +} + +function buildRelatedLinks( + pages: PageInfo[], + variant: "full" | "small", +): Record { + const filtered = + variant === "small" + ? pages.filter((p) => p.title !== "Testimonials") + : pages; + const grouped = new Map(); + for (const page of filtered) { + if (!grouped.has(page.groupLabel)) { + grouped.set(page.groupLabel, []); + } + grouped.get(page.groupLabel)!.push(page); + } + + const related: Record = {}; + + for (const groupPages of grouped.values()) { + if (groupPages.length < 2) continue; + + for (const page of groupPages) { + related[page.title] = groupPages + .filter((p) => p.slug !== page.slug) + .map((p) => `[${p.title}](/${p.slug})`); + } + } + + return related; +} + +function addRelatedLinks( + page: string, + relatedLinks: Record, +): string { + const titleMatch = page.match(/^# (.+)/); + if (!titleMatch) return page; + const title = titleMatch[1].trim(); + + const related = relatedLinks[title]; + if (related && related.length > 0) { + return ( + page.trimEnd() + + "\n\n### Related\n" + + related.map((r) => `- ${r}`).join("\n") + + "\n" + ); + } + return page; +} + +async function enhanceLlmsTxtIndex(outputDir: string): Promise { + const llmsTxtPath = path.join(outputDir, "llms.txt"); + try { + 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. Last updated: ${buildDate} -Website: https://www.rocketsim.app -Documentation: https://www.rocketsim.app/docs +Website: ${baseUrl} +Documentation: ${baseUrl}/docs GitHub: https://github.com/AvdLee/RocketSimApp Support: support@rocketsim.app @@ -62,36 +280,41 @@ Support: support@rocketsim.app - Xcode build insights and team analytics - Network speed throttling and Simulator airplane mode +## Context + +- [Product context](${baseUrl}/llms-ctx.txt): concise product overview, feature summary, and technical details for RocketSim + ## Documentation Sets -- [Abridged documentation](https://www.rocketsim.app/llms-small.txt): compact version with non-essential content removed -- [Complete documentation](https://www.rocketsim.app/llms-full.txt): full documentation for RocketSim +- [Abridged documentation](${baseUrl}/llms-small.txt): compact version with non-essential content removed +- [Complete documentation](${baseUrl}/llms-full.txt): full documentation for RocketSim ## Quick Start -- [Getting Started](https://www.rocketsim.app/docs/getting-started/onboarding) -- [Product Tour](https://www.rocketsim.app/docs/getting-started/product-tour-and-quick-demos) -- [FAQ](https://www.rocketsim.app/docs/support/faq) +- [Getting Started](${baseUrl}/docs/getting-started/onboarding) +- [Product Tour](${baseUrl}/docs/getting-started/product-tour-and-quick-demos) +- [FAQ](${baseUrl}/docs/support/faq) ## Notes - All documentation is automatically generated from the same source as the official docs - The "Last updated" date reflects when the documentation was last built and deployed `; - await writeFile(llmsTxtPath, enhancedLlmsTxt, "utf-8"); - } catch { - // File might not exist - } - }, - }, - }; + await writeFile(llmsTxtPath, enhancedLlmsTxt, "utf-8"); + } catch { + // File might not exist + } } -function transformLlmsTxt(content: string, variant: "full" | "small"): string { - // Preserve the header +function transformLlmsTxt( + content: string, + variant: "full" | "small", + toc: string, + relatedLinks: Record, +): string { + // Extract and discard the original header const systemMatch = content.match(/^([\s\S]*?<\/SYSTEM>\n\n)/); - const systemHeader = systemMatch?.[1] ?? ""; - const body = systemHeader ? content.slice(systemHeader.length) : content; + const body = systemMatch ? content.slice(systemMatch[1].length) : content; // Split into page sections (each starts with "# Title") const pages = body.split(/(?=^# )/m); @@ -111,7 +334,17 @@ function transformLlmsTxt(content: string, variant: "full" | "small"): string { return true; }); - let result = systemHeader + filteredPages.join(""); + // Add cross-reference links to relevant pages + const pagesWithRelated = filteredPages.map((page) => + addRelatedLinks(page, relatedLinks), + ); + + // Assemble with enriched SYSTEM header and TOC + const newSystemHeader = + variant === "full" ? FULL_SYSTEM_HEADER : SMALL_SYSTEM_HEADER; + + let result = + newSystemHeader + "\n\n" + toc + "\n\n" + pagesWithRelated.join(""); // Convert components to markdown links (handles multi-line tags) result = result.replace(//g, (match) => { @@ -146,9 +379,9 @@ function transformLlmsTxt(content: string, variant: "full" | "small"): string { // Convert to blockquotes result = result.replace( /^:::(tip|note|caution|danger)\n([\s\S]*?)^:::/gm, - (_match, type: string, content: string) => { + (_match, type: string, contentBlock: string) => { const label = type.charAt(0).toUpperCase() + type.slice(1); - const quoted = content + const quoted = contentBlock .trimEnd() .split("\n") .map((line) => `> ${line}`)