Skip to content

Commit 474d4bc

Browse files
committed
merge commit
2 parents 3503097 + 6d8f177 commit 474d4bc

102 files changed

Lines changed: 2019 additions & 941 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,5 @@ yarn-error.log*
221221
/versioned_docs/version-3.0/capacitor-sdk-migration-guides.md
222222
/versioned_docs/version-3.0/migration-to-capacitor-316.md
223223
/versioned_docs/version-3.0/sdk-installation-capacitor.md
224+
225+
.worktrees/

CLAUDE.md

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project overview
6+
7+
Adapty documentation site (codename Cassiopeia). Built with **Astro 5 + MDX + React + Tailwind CSS 4**. Covers 7 SDK platforms: iOS, Android, React Native, Flutter, Unity, Kotlin Multiplatform, Capacitor.
8+
9+
Deployed to **https://adapty.io/docs** via AWS S3 + CloudFront. GitHub Actions deploy `develop` → staging, `main` → production.
10+
11+
## Commands
12+
13+
```bash
14+
npm run dev # Dev server at localhost:4321 (runs prebuild + build:md first)
15+
npm run build # Production build → ./build/
16+
npm run preview # Serve production build locally
17+
```
18+
19+
No test suite or linter is configured. Package manager: **Yarn 1.22**.
20+
21+
## Content architecture
22+
23+
All articles live in `src/content/docs` as `.mdx` files. Subdirectories are for organization only — **URLs are derived from filename alone**, not folder path (e.g., `version-3.0/ios/sdk-installation-ios.mdx``/docs/sdk-installation-ios`).
24+
25+
### Frontmatter schema
26+
27+
```yaml
28+
title: "Required display title"
29+
description: "SEO description"
30+
metadataTitle: "Browser tab title | Adapty Docs"
31+
keywords: ['array', 'of', 'keywords']
32+
rank: 100 # Sort priority, default 50
33+
customSlug: "override-url" # Optional URL override
34+
```
35+
36+
### Navigation
37+
38+
Sidebars are defined per platform in `src/data/sidebars/*.json` (ios, android, react-native, flutter, unity, kmp, capacitor, tutorial, api). Each entry references an article by its filename-based `id`. To add an article to navigation, add its id to the appropriate sidebar JSON.
39+
40+
### Images
41+
42+
- Article-specific: `src/assets/{article-name}/image.png`
43+
- Shared: `src/assets/shared/image.png`
44+
- Use `<ZoomImage id="image.png" width="700px" alt="desc" />` (preferred)
45+
- Legacy `<Zoom><img src={require(...)}/></Zoom>` still works
46+
47+
### Path aliases
48+
49+
- `@site` → repo root
50+
- `@components` → `src/components/`
51+
52+
## Key components
53+
54+
| Component | Import required? | Usage |
55+
|-----------|-----------------|-------|
56+
| `ZoomImage` | Yes | `<ZoomImage id="file.png" width="700px" alt="..." />` |
57+
| `Tabs`/`TabItem` | Yes | `<Tabs groupId="platform"><TabItem value="ios" label="iOS">...</TabItem></Tabs>` |
58+
| `Details` | Yes | `<Details summary="Title">content</Details>` |
59+
| `InlineTooltip` | Yes | `<InlineTooltip tooltip="hover text">[link](page.md)</InlineTooltip>` |
60+
| `CustomDocCardList` | Yes | `<CustomDocCardList ids={['id1','id2']} />` or `<CustomDocCardList />` for auto |
61+
| `Button` | **No** (auto-registered) | `<Button id="page-id">Text</Button>` or `<Button href="url">Text</Button>` |
62+
| `Callout` | **No** (remark plugin) | `:::note`, `:::tip`, `:::info`, `:::warning`, `:::danger`, `:::important`, `:::link` |
63+
64+
Import path pattern: `import Component from '@site/src/components/Component.astro';`
65+
66+
## Reusable content snippets
67+
68+
`src/components/reusable/` contains MDX snippets that can be imported into multiple articles to avoid content duplication.
69+
70+
## Remark/Rehype plugins (`src/plugins/`)
71+
72+
- `remark-aside` — converts `:::note`/`:::tip`/etc. fenced directives into `<Callout>` components
73+
- `remark-transform-links` — strips `.md`/`.mdx` extensions from internal links
74+
- `remark-transform-require` — handles legacy `require()` image imports
75+
- `remark-transform-details` — processes `<Details>` components
76+
- `remark-heading-id` — auto-generates heading anchors
77+
- `remark-strip-imports` — removes imports during markdown export
78+
- `remark-strip-highlight-comments` — cleans highlight syntax
79+
80+
## Code blocks
81+
82+
````markdown
83+
```swift title="MyApp.swift" {2,4-6}
84+
// Line highlighting and title supported via Shiki transformers
85+
```
86+
````
87+
88+
## Learning and Memory Management
89+
90+
- YOU MUST use the journal tool frequently to capture technical insights, failed approaches, and user preferences
91+
- Before starting complex tasks, search the journal for relevant past experiences and lessons learned
92+
- Document architectural decisions and their outcomes for future reference
93+
- Track patterns in user feedback to improve collaboration over time
94+
- When you notice something that should be fixed but is unrelated to your current task, document it in your journal rather than fixing it immediately
95+
96+
## Styling architecture
97+
98+
### CSS files
99+
100+
| File | Role |
101+
|------|------|
102+
| `src/styles/global.css` | **Primary stylesheet.** Tailwind import, theme variables (`@theme`), light/dark CSS custom properties, all design block styles (code blocks, details, callouts, tables, zoom images, heading anchors, task lists, highlight lines) |
103+
| `src/css/custom.css` | Legacy Docusaurus-era variables (`--purplePrimary`, `--ifm-*`). Still loaded via `src/css/custom.scss` but superseded by `global.css` for new work |
104+
| `src/css/api-reference.css` | Styles for the Stoplight API reference pages |
105+
| `src/css/fonts/fonts.css` | `@font-face` declarations for Inter, Roboto, Fira Code |
106+
107+
### Theme system
108+
109+
- Light/dark mode toggled via `.dark` class on `<html>` (persisted in `localStorage`)
110+
- All design tokens are CSS custom properties defined in `:root` (light) and `.dark` (dark) blocks in `global.css`
111+
- Key tokens: `--bg-primary`, `--bg-secondary`, `--text-primary`, `--text-secondary`, `--accent-primary` (`#5c13ff` light / `#a78bfa` dark), `--border-primary`, `--shadow-sm/md/lg`
112+
- Brand color: `--color-primary-500: #6720ff`
113+
- Fonts: Inter (body), Fira Code (code blocks)
114+
- Tailwind 4 configured in `global.css` via `@theme` block (custom font sizes, line heights, brand colors)
115+
116+
### Design blocks in `global.css`
117+
118+
These are the styled visual blocks that articles use — their CSS lives entirely in `global.css`:
119+
120+
- **Code blocks** (`.code-block-wrapper`) — title bar, copy button, Shiki syntax highlighting, dark mode color inversion, diff styling, line highlighting (`.highlight-line`)
121+
- **Callouts** — rendered by remark-aside plugin into `<Callout>` (note/tip/info/warning/danger/important/link)
122+
- **Details/Accordion** (`details`/`summary`) — collapsible sections with chevron animation
123+
- **Ordered lists** (`.docs-prose ol`) — circular step-number bullets (Mintlify-inspired)
124+
- **Zoom images** (`.zoom-wrapper`, `.zoom-image`) — bordered, shadowed, hover-scale images
125+
- **Tables** — word-break handling, code wrapping within cells
126+
- **Heading anchors** (`.heading-anchor`) — hover-revealed link icon
127+
- **Task lists** (`li:has(input[type="checkbox"])`) — checkbox styling
128+
129+
### Layout
130+
131+
- Single layout: `src/layouts/DocsLayout.astro` — assembles Header, Sidebar, Breadcrumbs, article content, TableOfContents, FeedbackForm, Footer
132+
- Layout applies Tailwind prose classes (`.docs-prose`) to article content
133+
- Right column (ToC + feedback) visible at `xl:` (1280px+), sidebar at `lg:` (1024px+)
134+
- API reference pages use `isFullWidth` mode (no sidebar/ToC)
135+
136+
### Page routing
137+
138+
- `src/pages/[...slug].astro` — main catch-all route for doc articles
139+
- `src/pages/[slug].astro` + `src/pages/[slug]/[...rest].astro` — API reference pages
140+
141+
### UI components (non-content)
142+
143+
These are layout/interactive components in `src/components/`, not imported by article authors:
144+
145+
- `Header.astro` — top nav with platform switcher, search, theme toggle
146+
- `Sidebar.astro` / `SidebarItem.astro` — left navigation tree
147+
- `PlatformSwitcher.astro` — SDK platform selector in header
148+
- `Search.astro` — Algolia-powered search
149+
- `ThemeToggle.astro` — light/dark mode switch
150+
- `TableOfContents.astro` — right-column heading navigation
151+
- `FeedbackForm.astro` — page feedback widget
152+
- `Breadcrumbs.astro` — breadcrumb trail
153+
- `Footer.astro` — page footer
154+
- `ZoomLightbox.astro` — fullscreen image lightbox overlay
155+
- `Calculator.tsx` — interactive React calculator widget
156+
- `ApiReferencePage.astro` — Stoplight Elements API docs wrapper
157+
158+
## Build pipeline details
159+
160+
- `prebuild` copies shared assets (images, API specs) to `public/`
161+
- `build:md` generates plain markdown exports and LLM-optimized files (`scripts/generate-md.mjs`, `generate-llms.mjs`, `generate-platform-llms-full.mjs`)
162+
- Production build runs `astro build` then `build:md:prod` (outputs to `./build/`)
163+
164+
## Reference
165+
166+
Comprehensive component examples and writing guidelines: `TECH_WRITERS_README.md`

context7.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"url": "https://context7.com/adaptyteam/adapty-docs",
3+
"public_key": "pk_DR8Ko4Aa3rd5yAaPAyqPE"
4+
}

scripts/generate-llms.mjs

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { fileURLToPath } from 'node:url';
44

55
const __dirname = path.dirname(fileURLToPath(import.meta.url));
66
const SIDEBARS_DIR = path.resolve(__dirname, '../src/data/sidebars');
7+
const DOCS_BASE = path.resolve(__dirname, '../src/content/docs');
78

89
// Get output dir from args or default to public
910
const targetDirName = process.argv[2] || '../public';
1011
const OUTPUT_DIR = path.resolve(__dirname, targetDirName);
1112

12-
const BASE_URL = 'https://docs.adapty.io'; // Base URL for public links
13+
const BASE_URL = 'https://adapty.io/docs'; // Base URL for public links
1314

1415
// Helper: Ensure directory exists
1516
async function ensureDir(dir) {
@@ -20,19 +21,80 @@ async function ensureDir(dir) {
2021
}
2122
}
2223

24+
// Find a doc file by searching all subdirectories under DOCS_BASE
25+
async function findDocFile(docId) {
26+
const filename = `${docId}.mdx`;
27+
async function search(dir) {
28+
const entries = await fs.readdir(dir, { withFileTypes: true });
29+
for (const entry of entries) {
30+
if (entry.isDirectory()) {
31+
const result = await search(path.join(dir, entry.name));
32+
if (result) return result;
33+
} else if (entry.name === filename) {
34+
return path.join(dir, entry.name);
35+
}
36+
}
37+
return null;
38+
}
39+
return search(DOCS_BASE);
40+
}
41+
42+
// Extract description from MDX frontmatter
43+
function extractDescription(content) {
44+
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
45+
if (!match) return null;
46+
const fmMatch = match[1].match(/^description:\s*["']?(.*?)["']?\s*$/m);
47+
return fmMatch ? fmMatch[1] : null;
48+
}
49+
50+
// Collect doc IDs from sidebar items recursively
51+
function collectDocIds(items, ids) {
52+
for (const item of items) {
53+
if (item.type === 'category') {
54+
if (item.link && item.link.type === 'doc') ids.add(item.link.id);
55+
if (item.items) collectDocIds(item.items, ids);
56+
} else if (item.type === 'doc' && item.id) {
57+
ids.add(item.id);
58+
}
59+
}
60+
}
61+
62+
// Build a map of docId → description from all sidebar doc IDs
63+
async function buildDescriptionMap(sidebarFiles) {
64+
const descriptions = new Map();
65+
66+
const allDocIds = new Set();
67+
for (const file of sidebarFiles) {
68+
if (!file.endsWith('.json')) continue;
69+
const content = await fs.readFile(path.join(SIDEBARS_DIR, file), 'utf-8');
70+
const data = JSON.parse(content);
71+
const items = Array.isArray(data) ? data : [];
72+
collectDocIds(items, allDocIds);
73+
}
74+
75+
for (const docId of allDocIds) {
76+
const filePath = await findDocFile(docId);
77+
if (filePath) {
78+
try {
79+
const content = await fs.readFile(filePath, 'utf-8');
80+
const desc = extractDescription(content);
81+
if (desc) descriptions.set(docId, desc);
82+
} catch { /* skip */ }
83+
}
84+
}
85+
86+
return descriptions;
87+
}
88+
2389
// Recursive function to parse sidebar items
24-
function parseItems(items, depth = 0) {
90+
function parseItems(items, descriptions, depth = 0) {
2591
let output = '';
2692
const indent = ' '.repeat(depth);
2793

2894
for (const item of items) {
2995
if (item.type === 'category') {
3096
// Add Category Header
3197
if (item.label) {
32-
// Use consistent markdown header levels based on depth
33-
// Tier 1: ### Label, Tier 2: #### Label, etc?
34-
// Or just use bullet points for hierarchy which is often cleaner for LLMs.
35-
// Let's use headers for top level, bullets for nested.
3698
if (depth === 0) {
3799
output += `\n### ${item.label}\n\n`;
38100
} else {
@@ -42,16 +104,20 @@ function parseItems(items, depth = 0) {
42104

43105
// If category has a link, add it as a doc item too
44106
if (item.link && item.link.type === 'doc') {
45-
output += `${indent} - [Overview](${BASE_URL}/${item.link.id}.md)\n`;
107+
const desc = descriptions.get(item.link.id);
108+
const suffix = desc ? `: ${desc}` : '';
109+
output += `${indent} - [Overview](${BASE_URL}/${item.link.id}.md)${suffix}\n`;
46110
}
47111

48112
// Process children
49113
if (item.items) {
50-
output += parseItems(item.items, depth + 1);
114+
output += parseItems(item.items, descriptions, depth + 1);
51115
}
52116

53117
} else if (item.type === 'doc') {
54-
output += `${indent}- [${item.label || item.id}](${BASE_URL}/${item.id}.md)\n`;
118+
const desc = descriptions.get(item.id);
119+
const suffix = desc ? `: ${desc}` : '';
120+
output += `${indent}- [${item.label || item.id}](${BASE_URL}/${item.id}.md)${suffix}\n`;
55121
} else if (item.type === 'link') {
56122
// External or manual link
57123
output += `${indent}- [${item.label}](${item.href})\n`;
@@ -64,8 +130,11 @@ async function generateLLMFiles() {
64130
await ensureDir(OUTPUT_DIR);
65131
const files = await fs.readdir(SIDEBARS_DIR);
66132

67-
let allContent = '# Full Documentation Index\n\n';
68-
let mainContent = '';
133+
// Build descriptions map upfront
134+
const descriptions = await buildDescriptionMap(files);
135+
console.log(`Loaded ${descriptions.size} descriptions from doc files`);
136+
137+
let allContent = '# Adapty Documentation\n\n> Adapty is an in-app purchase platform for mobile apps. It handles subscriptions, one-time purchases, and consumables — from purchase processing and receipt validation to analytics, A/B testing, and integrations.\n\n';
69138

70139
for (const file of files) {
71140
if (!file.endsWith('.json')) continue;
@@ -76,14 +145,8 @@ async function generateLLMFiles() {
76145

77146
// Generate content for this sidebar
78147
let sidebarContent = `# ${file.replace('.json', '')} Documentation\n\n`;
79-
// Handle array root (tutorial.json) vs object root (others might be different? usually array in Docusaurus/classic structures)
80-
// The file I read (tutorial.json) is an array. I'll assume all are arrays of items.
81-
82148
let items = Array.isArray(sidebarData) ? sidebarData : [];
83-
// If it's an object with 'items' or similar, handle that (Standard Docusaurus sidebars.js export object)
84-
// But here we just see JSON arrays in the file listing.
85-
86-
sidebarContent += parseItems(items);
149+
sidebarContent += parseItems(items, descriptions);
87150

88151
// Save Platform Specific File
89152
const platformName = file.replace('.json', '');
@@ -94,14 +157,9 @@ async function generateLLMFiles() {
94157
allContent += sidebarContent + '\n---\n\n';
95158
}
96159

97-
// Write Main llms.txt (now same as full or containing all sidebars as requested)
98-
// "llms.txt and llms-full.txt ... must contain content of all the sidebars"
160+
// Write Main llms.txt
99161
await fs.writeFile(path.join(OUTPUT_DIR, 'llms.txt'), allContent);
100162
console.log('Generated: llms.txt (all sidebars)');
101-
102-
// Write Full Index (redundant but requested)
103-
await fs.writeFile(path.join(OUTPUT_DIR, 'llms-full.txt'), allContent);
104-
console.log('Generated: llms-full.txt');
105163
}
106164

107165
generateLLMFiles().catch(console.error);

scripts/generate-md.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ async function processFiles(dir, reusableComponents) {
118118
const destPath = path.join(OUTPUT_DIR, `${basename}.md`);
119119

120120
await fs.writeFile(destPath, content, 'utf-8');
121-
console.log(`Generated: ${basename}.md`);
122121
}
123122
}
124123
}

0 commit comments

Comments
 (0)