Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v20
v24
34 changes: 21 additions & 13 deletions apps/docs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
# Dependencies
# deps
/node_modules

# Production
/build
# generated content
.source
content/docs/api

# Generated files
.docusaurus
.cache-loader
# test & build
/coverage
/.next/
/out/
/build
*.tsbuildinfo

# Misc
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

*.pem
/.pnp
.pnp.js
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*

# others
.env*.local
.vercel
next-env.d.ts
55 changes: 30 additions & 25 deletions apps/docs/README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
# Website
# React Native Cloud Storage Documentation

This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
The documentation site for [`react-native-cloud-storage`](https://github.com/kuatsu/react-native-cloud-storage),
built with [Fumadocs](https://fumadocs.dev) on Next.js. Deployed to
[react-native-cloud-storage.oss.kuatsu.de](https://react-native-cloud-storage.oss.kuatsu.de).

### Installation
## Structure

```sh
$ pnpm install
```
- `content/docs/**` — authored MDX (installation, guides, example).
- `content/docs/api/**` — **generated** API reference (TypeDoc → Markdown). Git-ignored; do not edit by
hand. Regenerate with `pnpm docs:api`.
- `app/(home)` — landing page. `app/docs` — docs renderer. `lib/`, `config/`, `scripts/` — the
TypeDoc-to-Fumadocs pipeline and provider/platform badge machinery.

## Development

### Local Development
From the repo root (preferred, so workspace dependencies resolve):

```sh
$ pnpm start
pnpm --filter react-native-cloud-storage-docs dev
```

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.

### Build
Or from this directory:

```sh
$ pnpm build
pnpm dev # generate API reference, then start the dev server
pnpm docs:api # (re)generate content/docs/api from the library's TSDoc
pnpm build # production build
pnpm typecheck # generate API + types, then tsc --noEmit
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

### Deployment
`dev`, `build`, and `typecheck` run `pnpm docs:api` first, so the generated API reference is always in
sync with `packages/react-native-cloud-storage/src`.

Using SSH:
## API reference & badges

```sh
$ USE_SSH=true pnpm deploy
```

Not using SSH:
The API reference is generated from the library's TypeScript and TSDoc by TypeDoc
(`typedoc.config.mts` + `scripts/typedoc-frontmatter.mts`). Provider/platform badges come from
additive `@platform` / `@provider` TSDoc tags in the library source; never edit the generated MDX.

```sh
$ GIT_USER=<Your GitHub username> pnpm deploy
```
## Deployment

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
Deployed on Netlify. Configuration lives in `netlify.toml` (base directory `apps/docs`, build command
`pnpm build`, publish directory `.next`, `@netlify/plugin-nextjs`). Netlify installs workspace
dependencies from the repo-root pnpm lockfile. If the site is instead configured through the Netlify
UI (as for other Kuatsu OSS sites), mirror those same values there.
6 changes: 6 additions & 0 deletions apps/docs/app/(home)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/lib/layout.shared';

export default function Layout({ children }: LayoutProps<'/'>) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

LayoutProps<'/'>undefined — same cross-file TypeScript error as root layout.

This file also uses the undefined LayoutProps<'/'> type. Once the type is defined and imported globally (or in a layout types file), apply it consistently across all layout files: apps/docs/app/layout.tsx, apps/docs/app/(home)/layout.tsx, and apps/docs/app/docs/layout.tsx.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/docs/app/`(home)/layout.tsx at line 4, The Layout function in this file
uses the undefined type LayoutProps<'/'> which is causing a TypeScript error.
Define the LayoutProps type in a centralized layout types file or global types
configuration, then import this type into all layout files including the Layout
function in apps/docs/app/layout.tsx, apps/docs/app/(home)/layout.tsx, and
apps/docs/app/docs/layout.tsx. Apply the LayoutProps type consistently across
all these Layout function component signatures to resolve the undefined type
error across all layout files.

return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
}
149 changes: 149 additions & 0 deletions apps/docs/app/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import type { ReactNode } from 'react';
import Link from 'next/link';
import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
import { ArrowRight, Cloud, FolderTree, Github, Layers, Puzzle, Webhook } from 'lucide-react';
import { gitConfig } from '@/lib/layout.shared';

const githubUrl = `https://github.com/${gitConfig.user}/${gitConfig.repo}`;

function InlineCode({ children }: { children: ReactNode }) {
return (
<code className="rounded-md border border-fd-border bg-fd-muted px-1 py-0.5 font-mono text-[0.85em]">
{children}
</code>
);
}

const codeSample = `import { CloudStorage } from 'react-native-cloud-storage';

// Write a file to the cloud...
await CloudStorage.writeFile('/data.json', data.toString());

// ... and read it back!
const data = await CloudStorage.readFile('/data.json');`;

const features: { icon: typeof FolderTree; title: string; description: ReactNode }[] = [
{
icon: FolderTree,
title: 'fs-like API',
description: (
<>
Read, write, stat, and list files with an API that follows Node's <InlineCode>fs</InlineCode> conventions.
</>
),
},
{
icon: Cloud,
title: 'iCloud & Google Drive',
description: 'One API, two providers. Use the default platform backend, or pick your own.',
},
{
icon: Webhook,
title: 'React hooks',
description: (
<>
<InlineCode>useCloudFile</InlineCode> and <InlineCode>useIsCloudAvailable</InlineCode> keep your UI in sync with
cloud state.
</>
),
},
{
icon: Puzzle,
title: 'Expo config plugin',
description: 'Configure native capabilities automatically in Expo projects.',
},
];

const providers = [
{
name: 'iCloud (iOS only)',
scope: 'Backed by a native CloudKit module.',
},
{
name: 'Google Drive',
scope: 'Backed by the Drive REST API using an access token.',
},
];

export default function HomePage() {
return (
<main className="mx-auto flex w-full max-w-6xl flex-col gap-12 px-4 py-10 md:py-16">
<section className="grid items-center gap-8 rounded-3xl border border-fd-border bg-linear-to-br from-fd-primary/15 via-fd-background to-fd-background p-6 md:grid-cols-2 md:p-10">
<div className="flex flex-col items-start gap-5">
<p className="inline-flex items-center gap-2 rounded-full bg-fd-primary/12 px-3 py-1 text-sm font-medium text-fd-primary">
<Cloud className="size-4" />
React Native Cloud Storage
</p>
<h1 className="text-4xl font-semibold tracking-tight md:text-5xl">
iCloud and Google Drive for
<span className="text-fd-primary"> React Native.</span>
</h1>
<p className="max-w-xl text-fd-muted-foreground md:text-lg">
Use iCloud and Google Drive as file storage in your React Native app, with a single fs-like API, React
hooks, and an Expo config plugin.
</p>
<div className="flex flex-wrap items-center gap-3">
<Link
href="/docs"
className="inline-flex items-center gap-2 rounded-xl bg-fd-primary px-5 py-2.5 text-base font-medium text-fd-primary-foreground transition-opacity hover:opacity-90">
Get started
<ArrowRight className="size-4" />
</Link>
<a
href={githubUrl}
rel="noreferrer noopener"
target="_blank"
className="inline-flex items-center gap-2 rounded-xl border border-fd-border bg-fd-card px-5 py-2.5 text-base font-medium transition-colors hover:bg-fd-accent">
<Github className="size-4" />
GitHub
</a>
</div>
</div>
<div className="min-w-0 text-sm">
<DynamicCodeBlock lang="ts" code={codeSample} />
</div>
</section>

<section className="grid gap-4 md:grid-cols-2">
{features.map((feature) => (
<article key={feature.title} className="rounded-2xl border border-fd-border bg-fd-card p-5">
<p className="mb-2 inline-flex items-center gap-2 text-base font-medium">
<feature.icon className="size-5 text-fd-primary" />
{feature.title}
</p>
<p className="text-sm text-fd-muted-foreground">{feature.description}</p>
</article>
))}
</section>

<section className="grid gap-4 md:grid-cols-2">
{providers.map((provider) => (
<article
key={provider.name}
className="flex items-start gap-4 rounded-2xl border border-fd-border bg-fd-card p-5">
<span className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-fd-primary/10 text-fd-primary">
<Layers className="size-5" />
</span>
<div>
<p className="text-base font-medium">{provider.name}</p>
<p className="text-sm text-fd-muted-foreground">{provider.scope}</p>
</div>
</article>
))}
</section>

<section className="flex flex-col items-center gap-4 rounded-3xl border border-fd-border bg-fd-card p-8 text-center">
<h2 className="text-2xl font-semibold tracking-tight">Ready to back up your app's files?</h2>
<p className="max-w-xl text-fd-muted-foreground">
Install the library, follow the platform setup, and start reading and writing cloud files in minutes.
</p>
<Link
href="/docs"
className="inline-flex items-center gap-2 rounded-xl bg-fd-primary px-5 py-2.5 text-base font-medium text-fd-primary-foreground transition-opacity hover:opacity-90">
Read the docs
<ArrowRight className="size-4" />
</Link>
</section>
</main>
);
}
7 changes: 7 additions & 0 deletions apps/docs/app/api/search/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';

export const { GET } = createFromSource(source, {
// https://docs.orama.com/docs/orama-js/supported-languages
language: 'english',
});
67 changes: 67 additions & 0 deletions apps/docs/app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { getPageImage, source } from '@/lib/source';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/mdx-components';
import type { Metadata } from 'next';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import { LLMCopyButton, ViewOptions } from '@/components/ai/page-actions';
import { BadgePills } from '@/components/badges/pills';
import { readBadgesFromPageData, readTocBadgesFromPageData } from '@/lib/badges';
import { gitConfig } from '@/lib/layout.shared';

export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();

const MDX = page.data.body;
const badges = readBadgesFromPageData(page.data);
const tocBadges = readTocBadgesFromPageData(page.data);
const isApiPage = page.path.startsWith('api/');

// API pages are generated from the library source, so link "Open in GitHub" to the package source
// rather than the (gitignored) generated MDX file.
const githubUrl = isApiPage
? `https://github.com/${gitConfig.user}/${gitConfig.repo}/tree/${gitConfig.branch}/packages/react-native-cloud-storage/src`
: `https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/apps/docs/content/docs/${page.path}`;

return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<BadgePills badges={badges} />
<DocsDescription className="mb-0">{page.data.description}</DocsDescription>
<div className="flex flex-row gap-2 items-center border-b pb-6">
<LLMCopyButton markdownUrl={`${page.url}.mdx`} />
<ViewOptions markdownUrl={`${page.url}.mdx`} githubUrl={githubUrl} />
</div>
<DocsBody className={isApiPage ? 'api-reference-body' : undefined}>
<MDX
components={getMDXComponents(
{
a: createRelativeLink(source, page),
},
{ tocBadges }
)}
/>
</DocsBody>
</DocsPage>
);
}

export async function generateStaticParams() {
return source.generateParams();
}

export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();

return {
title: page.data.title,
description: page.data.description,
openGraph: {
images: getPageImage(page).url,
},
};
}
11 changes: 11 additions & 0 deletions apps/docs/app/docs/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions } from '@/lib/layout.shared';

export default function Layout({ children }: LayoutProps<'/docs'>) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

LayoutProps<'/docs'>undefined — same cross-file TypeScript error.

This file repeats the undefined LayoutProps type used in other layouts. Fix the type definition/import once and apply consistently across all layout files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/docs/app/docs/layout.tsx` at line 5, The `LayoutProps` type used in the
Layout function export is undefined and needs to be properly imported or
defined. Identify where the `LayoutProps` type should come from (check other
layout files or type definition files in the project), then add the correct
import statement to apps/docs/app/docs/layout.tsx. Once fixed in this file,
apply the same import/type definition solution consistently across all other
layout files that use `LayoutProps` to resolve the cross-file TypeScript error.

return (
<DocsLayout tree={source.getPageTree()} {...baseOptions()}>
{children}
</DocsLayout>
);
}
File renamed without changes.
Loading