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
843 changes: 416 additions & 427 deletions package-lock.json

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "bitremote-website",
"private": true,
"version": "1.1.2",
"version": "1.2.0",
"scripts": {
"dev": "next dev",
"build": "next build --webpack && node scripts/postprocess-seo-html.mjs",
Expand All @@ -10,18 +10,18 @@
},
"dependencies": {
"lenis": "^1.3.23",
"next": "^16.2.4",
"react": "^19.2.5",
"react-dom": "^19.2.5"
"next": "^16.2.6",
"react": "^19.2.6",
"react-dom": "^19.2.6"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.2.2",
"@types/node": "^25.6.0",
"@tailwindcss/postcss": "^4.3.0",
"@types/node": "^25.6.2",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"eslint": "^9",
"eslint-config-next": "^16.2.4",
"postcss": "^8.5.10",
"eslint": "^9.39.4",
"eslint-config-next": "^16.2.6",
"postcss": "^8.5.14",
"tailwindcss": "^4.2.2",
"typescript": "^6.0.3"
}
Expand Down
8 changes: 6 additions & 2 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ BitRemote is a remote download manager for Apple platforms. It connects to your

- [Home](https://bitremote.app/en/)
- [Terms](https://bitremote.app/en/terms/)
- [Privacy Policy](https://bitremote.app/en/terms/privacy/)
- [End User License Agreement](https://bitremote.app/en/terms/eula/)
- [Specified Commercial Transactions Act Disclosure](https://bitremote.app/en/terms/scta/)

## Legal

- [Privacy Policy](https://arkstudios.co.jp/en/privacy/bitremote)
- [End User License Agreement](https://arkstudios.co.jp/en/eula/bitremote)
- [Privacy Policy](https://bitremote.app/en/terms/privacy/)
- [End User License Agreement](https://bitremote.app/en/terms/eula/)
- [Specified Commercial Transactions Act Disclosure](https://bitremote.app/en/terms/scta/)

## Full Content

Expand Down
54 changes: 54 additions & 0 deletions src/app/[locale]/terms/eula/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { Metadata } from 'next';

import { LegalDocumentPage } from '@/components/LegalDocumentPage';
import { getLegalDocument, legalDocumentPathByKind } from '@/domain/legal-documents';
import { defaultLocale, isLocale, type Locale } from '@/i18n/locales';
import { getMessages } from '@/i18n/messages';
import { buildMetadataForCurrentLocalePage } from '@/seo/metadata';
import { buildBreadcrumbSchema, serializeJsonLd } from '@/seo/schema';

export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale: rawLocale } = await params;
const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale;
const messages = getMessages(locale);

return buildMetadataForCurrentLocalePage({
locale,
pathname: legalDocumentPathByKind.eula,
messages,
page: 'termsEula',
});
}

export default async function EulaPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale: rawLocale } = await params;
const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale;
const messages = getMessages(locale);
const document = getLegalDocument(locale, 'eula');
const breadcrumbSchema = buildBreadcrumbSchema({
locale,
items: [
{ name: messages.nav.home, path: '/' },
{ name: messages.pages.terms.title, path: '/terms/' },
{ name: document.title, path: legalDocumentPathByKind.eula },
],
});

return (
<main id="main-content">
<script
type="application/ld+json"
dangerouslySetInnerHTML={serializeJsonLd(breadcrumbSchema)}
/>
<LegalDocumentPage document={document} />
</main>
);
}
32 changes: 24 additions & 8 deletions src/app/[locale]/terms/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { Metadata } from 'next';

import { FadeInSection } from '@/components/ui/FadeInSection';
import { eulaUrl, privacyPolicyUrl } from '@/i18n/links';
import { legalDocumentPathByKind } from '@/domain/legal-documents';
import { defaultLocale, isLocale, type Locale } from '@/i18n/locales';
import { getMessages } from '@/i18n/messages';
import { localePath } from '@/i18n/urls';
import { buildMetadataForCurrentLocalePage } from '@/seo/metadata';
import { buildBreadcrumbSchema, serializeJsonLd } from '@/seo/schema';

Expand Down Expand Up @@ -32,8 +33,9 @@ export default async function TermsPage({
const { locale: rawLocale } = await params;
const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale;
const messages = getMessages(locale);
const eulaHref = eulaUrl(locale);
const privacyHref = privacyPolicyUrl(locale);
const eulaHref = localePath(locale, legalDocumentPathByKind.eula);
const privacyHref = localePath(locale, legalDocumentPathByKind.privacy);
const sctaHref = localePath(locale, legalDocumentPathByKind.scta);
const breadcrumbSchema = buildBreadcrumbSchema({
locale,
items: [
Expand Down Expand Up @@ -61,11 +63,9 @@ export default async function TermsPage({
</span>
))}
</p>
<div className="mt-8 grid gap-5 md:grid-cols-2">
<div className="mt-8 grid gap-5 md:grid-cols-3">
<a
href={privacyHref}
target="_blank"
rel="noreferrer"
className="group relative block rounded-[1.5rem] border border-[var(--color-border-soft)] bg-surface/70 p-6 text-inherit no-underline transition-all duration-300 ease-out hover:-translate-y-0.5 hover:border-[color-mix(in_srgb,var(--color-border-soft)_72%,var(--color-text-primary))] hover:bg-surface"
>
<span
Expand All @@ -84,8 +84,6 @@ export default async function TermsPage({

<a
href={eulaHref}
target="_blank"
rel="noreferrer"
className="group relative block rounded-[1.5rem] border border-[var(--color-border-soft)] bg-surface/70 p-6 text-inherit no-underline transition-all duration-300 ease-out hover:-translate-y-0.5 hover:border-[color-mix(in_srgb,var(--color-border-soft)_72%,var(--color-text-primary))] hover:bg-surface"
>
<span
Expand All @@ -101,6 +99,24 @@ export default async function TermsPage({
{messages.pages.terms.eulaBody}
</p>
</a>

<a
href={sctaHref}
className="group relative block rounded-[1.5rem] border border-[var(--color-border-soft)] bg-surface/70 p-6 text-inherit no-underline transition-all duration-300 ease-out hover:-translate-y-0.5 hover:border-[color-mix(in_srgb,var(--color-border-soft)_72%,var(--color-text-primary))] hover:bg-surface"
>
<span
aria-hidden="true"
className="absolute right-6 top-6 text-lg leading-none text-text-secondary transition-all duration-300 ease-out group-hover:translate-x-1 group-hover:text-text-primary"
>
</span>
<h2 className="m-0 text-lg font-semibold tracking-[-0.02em] text-text-primary">
{messages.pages.terms.sctaTitle}
</h2>
<p className="mb-0 mt-3 text-sm leading-6 text-text-secondary">
{messages.pages.terms.sctaBody}
</p>
</a>
</div>
</section>
</FadeInSection>
Expand Down
54 changes: 54 additions & 0 deletions src/app/[locale]/terms/privacy/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { Metadata } from 'next';

import { LegalDocumentPage } from '@/components/LegalDocumentPage';
import { getLegalDocument, legalDocumentPathByKind } from '@/domain/legal-documents';
import { defaultLocale, isLocale, type Locale } from '@/i18n/locales';
import { getMessages } from '@/i18n/messages';
import { buildMetadataForCurrentLocalePage } from '@/seo/metadata';
import { buildBreadcrumbSchema, serializeJsonLd } from '@/seo/schema';

export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale: rawLocale } = await params;
const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale;
const messages = getMessages(locale);

return buildMetadataForCurrentLocalePage({
locale,
pathname: legalDocumentPathByKind.privacy,
messages,
page: 'termsPrivacy',
});
}

export default async function PrivacyPolicyPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale: rawLocale } = await params;
const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale;
const messages = getMessages(locale);
const document = getLegalDocument(locale, 'privacy');
const breadcrumbSchema = buildBreadcrumbSchema({
locale,
items: [
{ name: messages.nav.home, path: '/' },
{ name: messages.pages.terms.title, path: '/terms/' },
{ name: document.title, path: legalDocumentPathByKind.privacy },
],
});

return (
<main id="main-content">
<script
type="application/ld+json"
dangerouslySetInnerHTML={serializeJsonLd(breadcrumbSchema)}
/>
<LegalDocumentPage document={document} />
</main>
);
}
54 changes: 54 additions & 0 deletions src/app/[locale]/terms/scta/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { Metadata } from 'next';

import { LegalDocumentPage } from '@/components/LegalDocumentPage';
import { getLegalDocument, legalDocumentPathByKind } from '@/domain/legal-documents';
import { defaultLocale, isLocale, type Locale } from '@/i18n/locales';
import { getMessages } from '@/i18n/messages';
import { buildMetadataForCurrentLocalePage } from '@/seo/metadata';
import { buildBreadcrumbSchema, serializeJsonLd } from '@/seo/schema';

export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale: rawLocale } = await params;
const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale;
const messages = getMessages(locale);

return buildMetadataForCurrentLocalePage({
locale,
pathname: legalDocumentPathByKind.scta,
messages,
page: 'termsScta',
});
}

export default async function SpecifiedCommercialTransactionsActPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale: rawLocale } = await params;
const locale: Locale = isLocale(rawLocale) ? rawLocale : defaultLocale;
const messages = getMessages(locale);
const document = getLegalDocument(locale, 'scta');
const breadcrumbSchema = buildBreadcrumbSchema({
locale,
items: [
{ name: messages.nav.home, path: '/' },
{ name: messages.pages.terms.title, path: '/terms/' },
{ name: document.title, path: legalDocumentPathByKind.scta },
],
});

return (
<main id="main-content">
<script
type="application/ld+json"
dangerouslySetInnerHTML={serializeJsonLd(breadcrumbSchema)}
/>
<LegalDocumentPage document={document} />
</main>
);
}
23 changes: 23 additions & 0 deletions src/app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { downloaderSlugByDownloader, getDownloaderLandingContent } from '@/domain/downloader-landings';
import { supportedDownloaders } from '@/domain/downloaders';
import { getLegalDocument, legalDocumentPathByKind } from '@/domain/legal-documents';
import { LINKS } from '@/i18n/links';
import { getMessages } from '@/i18n/messages';
import { absoluteUrl } from '@/i18n/urls';
Expand All @@ -22,6 +23,9 @@ export function GET() {
sections.push(`- GitHub: ${LINKS.github}`);
sections.push(`- Discord: ${LINKS.discord}`);
sections.push(`- Telegram: ${LINKS.telegram}`);
sections.push(`- Privacy Policy: ${absoluteUrl(`/en${legalDocumentPathByKind.privacy}`)}`);
sections.push(`- End User License Agreement: ${absoluteUrl(`/en${legalDocumentPathByKind.eula}`)}`);
sections.push(`- Specified Commercial Transactions Act Disclosure: ${absoluteUrl(`/en${legalDocumentPathByKind.scta}`)}`);

// Features
sections.push(`\n## Features\n`);
Expand Down Expand Up @@ -76,6 +80,25 @@ export function GET() {
sections.push('');
}

// Legal Documents
sections.push(`## Legal Documents\n`);
for (const kind of ['privacy', 'eula', 'scta'] as const) {
const document = getLegalDocument('en', kind);
sections.push(`### ${document.title}\n`);
sections.push(document.intro);
sections.push('');
for (const section of document.sections) {
sections.push(`#### ${section.title}\n`);
sections.push(section.content);
if (section.links) {
for (const link of section.links) {
sections.push(`- [${link.title}](${link.href})`);
}
}
sections.push('');
}
}

const body = sections.join('\n');

return new Response(body, {
Expand Down
Loading