diff --git a/.github/workflows/cf-pages.yml b/.github/workflows/cf-pages.yml index 85694ae..b43c59d 100644 --- a/.github/workflows/cf-pages.yml +++ b/.github/workflows/cf-pages.yml @@ -5,7 +5,7 @@ on: branches: - main paths: - - 'html/**' + - 'web/**' workflow_dispatch: jobs: @@ -19,12 +19,25 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + working-directory: web + + - name: Build + run: npm run build + working-directory: web + - name: Deploy to Cloudflare Pages uses: cloudflare/pages-action@v1 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }} - directory: html + directory: web/dist/aiat gitHubToken: ${{ secrets.GITHUB_TOKEN }} wranglerVersion: '3' diff --git a/.gitignore b/.gitignore index daca568..d85a12e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ deploy.yml claude.md embedded.mobileprovision .env -html.zip +web/dist +node_modules +package-lock.json diff --git a/html/about/support/app.html b/html/about/support/app.html deleted file mode 100644 index 241d905..0000000 --- a/html/about/support/app.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - App Info - Aiat - - - - -
- ← Back to syu.is -
- -
- Aiat -
Aiat
-
v1.111.0
-
- -
-

Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.

-
- -
-
App Information
-
-
-
Version
-
1.111.0
-
-
-
Category
-
Social
-
-
-
Supported OS
-
iOS 26.0+
-
-
-
Price
-
Free
-
-
-
- -
-
Developer
-
syui
- - -
- -
-
Bitcoin
-
- - 3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr - copy -
-
- - - - - - diff --git a/html/about/support/help.html b/html/about/support/help.html deleted file mode 100644 index 72da246..0000000 --- a/html/about/support/help.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - Help - syu.is - - - - -
- ← Back to syu.is -

Help Center

-
- -

About syu.is

-

syu.is is a social networking service built on the AT Protocol (Authenticated Transfer Protocol). It allows users to share content, connect with others, and participate in a decentralized social network.

- -

Frequently Asked Questions

- -
-

What is the AT Protocol?

-

The AT Protocol is a decentralized social networking protocol that allows users to own their data and identity. It enables federation between different services while maintaining user control.

-
- -
-

How do I create an account?

-

You can create an account by downloading the app or visiting the website. You'll need to provide an email address and choose a username.

-
- -
-

How do I reset my password?

-

You can reset your password through the login screen by selecting "Forgot Password" and following the instructions sent to your email.

-
- -
-

How do I delete my account?

-

You can delete your account through Settings > Account. Please note that account deletion is permanent and cannot be undone.

-
- -
-

How do I report abuse or inappropriate content?

-

You can report content by using the report function available on each post. Our moderation team will review reports and take appropriate action.

-
- -

Contact

-
-

For additional support or questions:

- -
- -

Related Links

- - - - - diff --git a/html/about/support/license.html b/html/about/support/license.html deleted file mode 100644 index c61217a..0000000 --- a/html/about/support/license.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - License - syu.is - - - - -
- ← Back to syu.is -

License

-
- -

Aiat (iOS/Android App)

-

This application is based on the Bluesky Social App, which is open source software.

- -

Open Source Licenses

-

This app uses the following open source software:

- -

Bluesky Social App

-

Licensed under the MIT License

-

https://github.com/bluesky-social/social-app

- -

AT Protocol

-

Licensed under the MIT License / Apache 2.0

-

https://github.com/bluesky-social/atproto

- -

Third Party Libraries

-

This application includes various third-party libraries, each with their own licenses. For a complete list, please see the application's source code repository.

- - - - diff --git a/html/about/support/privacy.html b/html/about/support/privacy.html deleted file mode 100644 index 9ab6925..0000000 --- a/html/about/support/privacy.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - Privacy Policy - syu.is - - - - -
- ← Back to syu.is -

Privacy Policy

-
- -

1. Introduction

-

This Privacy Policy explains how syu.is collects, uses, and protects your personal information when you use our service.

- -

2. Information We Collect

-

We collect the following types of information:

- - -

3. How We Use Your Information

-

We use your information to:

- - -

4. Data Sharing

-

As part of the AT Protocol federation, your public content may be shared with other servers in the network. We do not sell your personal information to third parties.

- -

5. Data Security

-

We implement appropriate security measures to protect your personal information. However, no method of transmission over the Internet is 100% secure.

- -

6. Your Rights

-

You have the right to:

- - -

7. Cookies

-

We use cookies and similar technologies to maintain your session and improve your experience.

- -

8. Changes to This Policy

-

We may update this Privacy Policy from time to time. We will notify you of any significant changes.

- -

9. Contact

-

For privacy-related questions, please visit our Help page.

- - - - diff --git a/html/about/support/tos.html b/html/about/support/tos.html deleted file mode 100644 index 3784cb1..0000000 --- a/html/about/support/tos.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - Terms of Service - syu.is - - - - -
- ← Back to syu.is -

Terms of Service

-
- -

1. Introduction

-

Welcome to syu.is. By using our service, you agree to these terms. Please read them carefully.

- -

2. Service Description

-

syu.is is a social networking service built on the AT Protocol. We provide a platform for users to share content and connect with others.

- -

3. User Responsibilities

-

As a user of syu.is, you agree to:

- - -

4. Content Guidelines

-

Users are responsible for the content they post. Prohibited content includes:

- - -

5. Privacy

-

Your privacy is important to us. Please review our Privacy Policy to understand how we handle your data.

- -

6. Disclaimer

-

The service is provided "as is" without warranties of any kind. We are not liable for any damages arising from your use of the service.

- -

7. Changes to Terms

-

We may update these terms from time to time. Continued use of the service after changes constitutes acceptance of the new terms.

- -

8. Contact

-

For questions about these terms, please visit our Help page.

- - - - diff --git a/html/index.html b/html/index.html deleted file mode 100644 index a9f880a..0000000 --- a/html/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - App Info - Aiat - - - - -
- ← Back to syu.is -
- -
- Aiat -
Aiat
-
v1.111.0
-
- -
-

Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.

-
- -
-
App Information
-
-
-
Version
-
1.111.2
-
-
-
Category
-
Social
-
-
-
Supported OS
-
iOS 26.0+
-
-
-
Price
-
Free
-
-
-
- -
-
Developer
-
syui
- - -
- -
-
Bitcoin
-
- - 3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr - copy -
-
- - - - - - diff --git a/ios/setup.zsh b/ios/setup.zsh index e9b4b64..838f9c4 100755 --- a/ios/setup.zsh +++ b/ios/setup.zsh @@ -204,20 +204,35 @@ function ios-setup-clone() { echo "Repository found: $target_dir" } -# Generate bskyweb templates from html/ source -# html/ is the source of truth, bskyweb templates are generated +# Generate bskyweb templates from web/ build output +# web/ is the source of truth, bskyweb templates are generated from dist/aiat/ function ios-generate-bskyweb-templates() { echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "🌐 Generating bskyweb templates from html/..." + echo "🌐 Generating bskyweb templates from web/..." - local html_src="$d/html/about/support" + local web_dir="$d/../web" local templates="$target_dir/bskyweb/templates" - local static_src="$d/html/static" local static_out="$target_dir/bskyweb/static" - # Check if html source exists - if [ ! -d "$html_src" ]; then - echo "⚠️ html/about/support not found, skipping template generation" + # Check if web directory exists + if [ ! -d "$web_dir" ]; then + echo "⚠️ web/ directory not found, skipping template generation" + return 1 + fi + + # Build web + echo " Building web..." + cd "$web_dir" + npm install --silent 2>/dev/null + npm run build --silent 2>/dev/null + cd "$d" + + local dist_src="$web_dir/dist/aiat" + local static_src="$web_dir/dist/aiat/static" + + # Check if build output exists + if [ ! -d "$dist_src" ]; then + echo "⚠️ web/dist/aiat not found, build may have failed" return 1 fi @@ -225,22 +240,18 @@ function ios-generate-bskyweb-templates() { mkdir -p "$templates" mkdir -p "$static_out" - # Convert html/ to bskyweb templates + # Convert web/dist/aiat/ to bskyweb templates + # Structure: tos/index.html -> about-tos.html # Add {{ staticCDNHost }} prefix to /static/ paths - for html_file in privacy.html license.html tos.html help.html app.html; do - if [ -f "$html_src/$html_file" ]; then - local template_name="about-${html_file}" + for page in privacy license tos help app; do + local src_file="$dist_src/$page/index.html" + if [ -f "$src_file" ]; then + local template_name="about-${page}.html" sed 's|href="/static/|href="{{ staticCDNHost }}/static/|g; s|src="/static/|src="{{ staticCDNHost }}/static/|g' \ - "$html_src/$html_file" > "$templates/$template_name" + "$src_file" > "$templates/$template_name" fi done - # Also generate about-app.html from index.html if exists - if [ -f "$d/html/index.html" ]; then - sed 's|href="/static/|href="{{ staticCDNHost }}/static/|g; s|src="/static/|src="{{ staticCDNHost }}/static/|g' \ - "$d/html/index.html" > "$templates/about-app.html" - fi - # Copy static assets if [ -d "$static_src" ]; then cp -f "$static_src/"* "$static_out/" 2>/dev/null diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..44e4a09 --- /dev/null +++ b/web/package.json @@ -0,0 +1,28 @@ +{ + "name": "app-pages", + "version": "1.0.0", + "type": "module", + "scripts": { + "build": "tsx src/build.ts", + "watch": "tsx watch src/build.ts", + "serve": "npx serve dist", + "serve:aiat": "npx serve dist/aiat -p 3000", + "serve:aicard": "npx serve dist/aicard -p 3001", + "serve:airse": "npx serve dist/airse -p 3002", + "dev": "npm run build && npx concurrently \"npm run watch\" \"npm run serve:aiat\"", + "dev:aiat": "npm run build && npx concurrently \"npm run watch\" \"npm run serve:aiat\"", + "dev:aicard": "npm run build && npx concurrently \"npm run watch\" \"npm run serve:aicard\"", + "dev:airse": "npm run build && npx concurrently \"npm run watch\" \"npm run serve:airse\"" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "concurrently": "^9.0.0", + "serve": "^14.0.0", + "tsx": "^4.0.0", + "typescript": "^5.0.0" + }, + "dependencies": { + "marked": "^15.0.0", + "gray-matter": "^4.0.3" + } +} diff --git a/html/static/app.png b/web/src/assets/aiat/app.png similarity index 100% rename from html/static/app.png rename to web/src/assets/aiat/app.png diff --git a/web/src/assets/aiat/icon.png b/web/src/assets/aiat/icon.png new file mode 100644 index 0000000..f8f45fb Binary files /dev/null and b/web/src/assets/aiat/icon.png differ diff --git a/web/src/assets/aicard/app.png b/web/src/assets/aicard/app.png new file mode 100644 index 0000000..69a6840 Binary files /dev/null and b/web/src/assets/aicard/app.png differ diff --git a/web/src/assets/aicard/icon.png b/web/src/assets/aicard/icon.png new file mode 100644 index 0000000..69a6840 Binary files /dev/null and b/web/src/assets/aicard/icon.png differ diff --git a/web/src/assets/airse/app.png b/web/src/assets/airse/app.png new file mode 100644 index 0000000..5a74926 Binary files /dev/null and b/web/src/assets/airse/app.png differ diff --git a/web/src/assets/airse/icon.png b/web/src/assets/airse/icon.png new file mode 100644 index 0000000..5a74926 Binary files /dev/null and b/web/src/assets/airse/icon.png differ diff --git a/html/static/favicon.png b/web/src/assets/common/favicon.png similarity index 100% rename from html/static/favicon.png rename to web/src/assets/common/favicon.png diff --git a/web/src/build.ts b/web/src/build.ts new file mode 100644 index 0000000..83e273f --- /dev/null +++ b/web/src/build.ts @@ -0,0 +1,124 @@ +import { readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync, existsSync } from 'fs' +import { join, dirname } from 'path' +import { fileURLToPath } from 'url' +import { marked } from 'marked' +import matter from 'gray-matter' +import type { SiteConfig, PageMeta } from './types.js' +import { appLayout, legalLayout, oauthCallback, clientMetadata } from './templates/index.js' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const sitesDir = join(__dirname, 'sites') +const contentDir = join(__dirname, 'content/pages') +const assetsDir = join(__dirname, 'assets') +const distDir = join(__dirname, '..', 'dist') + +// Load all site configs +function loadSites(): SiteConfig[] { + const files = readdirSync(sitesDir).filter(f => f.endsWith('.json')) + return files.map(f => { + const content = readFileSync(join(sitesDir, f), 'utf-8') + return JSON.parse(content) as SiteConfig + }) +} + +// Load all markdown pages +function loadPages(): Array<{ meta: PageMeta & { path: string }, content: string }> { + const files = readdirSync(contentDir).filter(f => f.endsWith('.md')) + return files.map(f => { + const raw = readFileSync(join(contentDir, f), 'utf-8') + const { data, content } = matter(raw) + return { + meta: data as PageMeta & { path: string }, + content: marked(content) as string + } + }) +} + +// Ensure directory exists +function ensureDir(filePath: string): void { + mkdirSync(dirname(filePath), { recursive: true }) +} + +// Copy assets for a site +function copyAssets(site: SiteConfig): void { + const siteAssetsDir = join(assetsDir, site.id) + const commonAssetsDir = join(assetsDir, 'common') + const outDir = join(distDir, site.id, 'static') + + ensureDir(join(outDir, '_')) + + // Copy common assets first + if (existsSync(commonAssetsDir)) { + const files = readdirSync(commonAssetsDir) + for (const file of files) { + copyFileSync(join(commonAssetsDir, file), join(outDir, file)) + console.log(` -> static/${file} (common)`) + } + } + + // Copy site-specific assets (overrides common) + if (existsSync(siteAssetsDir)) { + const files = readdirSync(siteAssetsDir) + for (const file of files) { + copyFileSync(join(siteAssetsDir, file), join(outDir, file)) + console.log(` -> static/${file}`) + } + } +} + +// Build site +function buildSite(site: SiteConfig, pages: ReturnType): void { + const siteDir = join(distDir, site.id) + + console.log(`Building ${site.name} (${site.domain})...`) + + // Build pages from markdown + for (const page of pages) { + const outPath = join(siteDir, page.meta.path) + ensureDir(outPath) + + let html: string + if (page.meta.template === 'app') { + html = appLayout(site, page.content) + } else { + html = legalLayout(site, page.meta.title, page.content) + } + + writeFileSync(outPath, html) + console.log(` -> ${page.meta.path}`) + } + + // Build OAuth callback + const callbackPath = join(siteDir, 'oauth/callback/index.html') + ensureDir(callbackPath) + writeFileSync(callbackPath, oauthCallback(site)) + console.log(` -> oauth/callback/index.html`) + + // Build client metadata + const metadataPath = join(siteDir, '.well-known/client-metadata.json') + ensureDir(metadataPath) + writeFileSync(metadataPath, clientMetadata(site)) + console.log(` -> .well-known/client-metadata.json`) + + // Copy assets + copyAssets(site) +} + +// Main +function main(): void { + console.log('App Pages Builder\n') + + const sites = loadSites() + const pages = loadPages() + + console.log(`Found ${sites.length} sites, ${pages.length} pages\n`) + + for (const site of sites) { + buildSite(site, pages) + console.log('') + } + + console.log('Done!') +} + +main() diff --git a/web/src/content/pages/app.md b/web/src/content/pages/app.md new file mode 100644 index 0000000..d27753b --- /dev/null +++ b/web/src/content/pages/app.md @@ -0,0 +1,5 @@ +--- +title: App Info +template: app +path: app/index.html +--- diff --git a/web/src/content/pages/help.md b/web/src/content/pages/help.md new file mode 100644 index 0000000..2ba3804 --- /dev/null +++ b/web/src/content/pages/help.md @@ -0,0 +1,39 @@ +--- +title: Help +template: legal +path: help/index.html +--- + +## Getting Started + +1. Download the app from the App Store +2. Sign in with your AT Protocol handle +3. Start connecting with your community + +## Authentication + +This app uses AT Protocol OAuth for secure authentication. Your credentials are never stored on our servers. + +## Features + +- **Feed**: View posts from people you follow +- **Profile**: Customize your profile and view your posts +- **Search**: Find users and content + +## Troubleshooting + +### Login Issues + +- Ensure your handle is correct +- Check your internet connection +- Try logging out and back in + +### App Crashes + +- Update to the latest version +- Restart the app +- If issues persist, contact support + +## Contact Support + +For additional help, please visit our Git repository or contact us through AT Protocol. diff --git a/web/src/content/pages/index.md b/web/src/content/pages/index.md new file mode 100644 index 0000000..75d5f34 --- /dev/null +++ b/web/src/content/pages/index.md @@ -0,0 +1,5 @@ +--- +title: App Info +template: app +path: index.html +--- diff --git a/web/src/content/pages/license.md b/web/src/content/pages/license.md new file mode 100644 index 0000000..ee09f3e --- /dev/null +++ b/web/src/content/pages/license.md @@ -0,0 +1,29 @@ +--- +title: License +template: legal +path: license/index.html +--- + +## Software License + +This application is open source software. + +## Third-Party Libraries + +This app uses the following open source libraries: + +- **AT Protocol** - MIT License +- **React Native** - MIT License +- **Expo** - MIT License + +## Assets + +App icons and graphics are proprietary unless otherwise noted. + +## Attribution + +Built with AT Protocol by syui. + +## Contact + +For licensing questions, please contact us through our Git repository. diff --git a/web/src/content/pages/privacy.md b/web/src/content/pages/privacy.md new file mode 100644 index 0000000..9f7a5be --- /dev/null +++ b/web/src/content/pages/privacy.md @@ -0,0 +1,35 @@ +--- +title: Privacy Policy +template: legal +path: privacy/index.html +--- + +## Information We Collect + +This application collects minimal information necessary for its operation: + +- **Account Information**: Your AT Protocol handle and DID for authentication +- **Usage Data**: Basic app usage statistics to improve the service + +## How We Use Your Information + +- To provide and maintain the service +- To authenticate your identity +- To improve user experience + +## Data Storage + +Your data is stored securely on AT Protocol compatible servers. We do not sell or share your personal information with third parties. + +## Your Rights + +You have the right to: +- Access your personal data +- Delete your account and associated data +- Export your data + +## Contact + +If you have questions about this privacy policy, please contact us through our Git repository. + +*Last updated: 2025-01-15* diff --git a/web/src/content/pages/tos.md b/web/src/content/pages/tos.md new file mode 100644 index 0000000..3d5cfdf --- /dev/null +++ b/web/src/content/pages/tos.md @@ -0,0 +1,40 @@ +--- +title: Terms of Service +template: legal +path: tos/index.html +--- + +## Acceptance of Terms + +By using this application, you agree to these Terms of Service. + +## Use of Service + +You agree to use this service in accordance with: +- Applicable laws and regulations +- AT Protocol community guidelines +- Respectful behavior towards other users + +## User Content + +You retain ownership of content you create. By posting content, you grant us a license to display it within the service. + +## Prohibited Activities + +- Harassment or abuse of other users +- Spam or automated abuse +- Attempting to compromise the service + +## Termination + +We reserve the right to terminate accounts that violate these terms. + +## Disclaimer + +This service is provided "as is" without warranties of any kind. + +## Changes + +We may update these terms from time to time. Continued use constitutes acceptance of changes. + +*Last updated: 2025-01-15* diff --git a/web/src/sites/aiat.json b/web/src/sites/aiat.json new file mode 100644 index 0000000..d43259c --- /dev/null +++ b/web/src/sites/aiat.json @@ -0,0 +1,13 @@ +{ + "id": "aiat", + "name": "Aiat", + "domain": "at.syui.ai", + "scheme": "aiat", + "version": "1.111.0", + "category": "Social", + "description": "Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.", + "backLink": "syu.is", + "icon": "/static/app.png", + "os": "iOS 26.0+", + "price": "Free" +} diff --git a/web/src/sites/aicard.json b/web/src/sites/aicard.json new file mode 100644 index 0000000..beef462 --- /dev/null +++ b/web/src/sites/aicard.json @@ -0,0 +1,13 @@ +{ + "id": "aicard", + "name": "Aicard", + "domain": "card.syui.ai", + "scheme": "aicard", + "version": "1.0.0", + "category": "Game", + "description": "Aicard is a card collection game integrated with AT Protocol.", + "backLink": "syui.ai", + "icon": "/static/app.png", + "os": "iOS 26.0+", + "price": "Free" +} diff --git a/web/src/sites/airse.json b/web/src/sites/airse.json new file mode 100644 index 0000000..f2b0626 --- /dev/null +++ b/web/src/sites/airse.json @@ -0,0 +1,13 @@ +{ + "id": "airse", + "name": "Airse", + "domain": "rse.syui.ai", + "scheme": "airse", + "version": "1.0.0", + "category": "Game", + "description": "Airse is an adventure game built with Unreal Engine, featuring AT Protocol integration.", + "backLink": "syui.ai", + "icon": "/static/app.png", + "os": "Windows / macOS", + "price": "Free" +} diff --git a/web/src/templates/index.ts b/web/src/templates/index.ts new file mode 100644 index 0000000..ac716f2 --- /dev/null +++ b/web/src/templates/index.ts @@ -0,0 +1,3 @@ +export { layout, appLayout, legalLayout } from './layout.js' +export { oauthCallback, clientMetadata } from './oauth.js' +export { baseStyles } from './styles.js' diff --git a/web/src/templates/layout.ts b/web/src/templates/layout.ts new file mode 100644 index 0000000..ad33caa --- /dev/null +++ b/web/src/templates/layout.ts @@ -0,0 +1,105 @@ +import type { SiteConfig } from '../types.js' +import { baseStyles } from './styles.js' + +export function layout(site: SiteConfig, title: string, content: string): string { + return ` + + + + + ${title} - ${site.name} + + + + +
+ ← Back to ${site.backLink} +
+ ${content} + + +` +} + +export function appLayout(site: SiteConfig, content: string): string { + return layout(site, 'App Info', ` +
+ ${site.name} +
${site.name}
+
+ +
+

${site.description}

+
+ +
+
App Information
+
+
+
Category
+
${site.category}
+
+
+
Supported OS
+
${site.os}
+
+
+
Price
+
${site.price}
+
+
+
+ +
+
Developer
+
syui
+ + +
+ +
+
Bitcoin
+
+ + 3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr + copy +
+
+ + ${content} + + +`) +} + +export function legalLayout(site: SiteConfig, title: string, content: string): string { + return layout(site, title, ` +
+

${title}

+ ${content} +
+`) +} diff --git a/web/src/templates/oauth.ts b/web/src/templates/oauth.ts new file mode 100644 index 0000000..37c57d6 --- /dev/null +++ b/web/src/templates/oauth.ts @@ -0,0 +1,46 @@ +import type { SiteConfig } from '../types.js' + +export function oauthCallback(site: SiteConfig): string { + return ` + + + Redirecting... + + + +

Redirecting to app...

+

If nothing happens, click + here to continue in browser.

+ +` +} + +export function clientMetadata(site: SiteConfig): string { + return JSON.stringify({ + client_id: `https://${site.domain}/.well-known/client-metadata.json`, + client_name: site.name, + client_uri: `https://${site.domain}`, + logo_uri: `https://${site.domain}/static/icon.png`, + redirect_uris: [ + `https://${site.domain}/oauth/callback` + ], + scope: 'atproto transition:generic', + grant_types: [ + 'authorization_code', + 'refresh_token' + ], + response_types: [ + 'code' + ], + token_endpoint_auth_method: 'none', + application_type: 'web', + dpop_bound_access_tokens: true + }, null, 2) +} diff --git a/web/src/templates/styles.ts b/web/src/templates/styles.ts new file mode 100644 index 0000000..6fa052a --- /dev/null +++ b/web/src/templates/styles.ts @@ -0,0 +1,51 @@ +export const baseStyles = ` +* { box-sizing: border-box; margin: 0; padding: 0; } +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + line-height: 1.6; + color: #1a1a1a; + background: #fff; + padding: 20px; + max-width: 800px; + margin: 0 auto; +} +@media (prefers-color-scheme: dark) { + body { background: #000; color: #e0e0e0; } + a { color: #6bb3ff; } + h1, h2, h3 { color: #fff; } + .section { background: #1a1a1a; } + .info-item { background: #2a2a2a; } +} +.header { margin-bottom: 32px; } +.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; color: #0066cc; text-decoration: none; } +.back-link:hover { text-decoration: underline; } +.app-header { text-align: center; margin-bottom: 32px; } +.app-icon { width: 80px; height: 80px; border-radius: 18px; margin-bottom: 12px; } +.app-name { font-size: 24px; font-weight: bold; margin-bottom: 4px; } +.app-version { font-size: 14px; color: #666; } +.section { background: #f5f5f5; border-radius: 16px; padding: 20px; margin-bottom: 16px; } +.section-title { font-size: 13px; font-weight: 600; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; } +.description { font-size: 15px; line-height: 22px; } +.info-grid { display: flex; flex-wrap: wrap; gap: 8px; } +.info-item { flex: 1; min-width: 45%; text-align: center; background: #e8e8e8; border-radius: 12px; padding: 12px; } +.info-label { font-size: 11px; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; } +.info-value { font-size: 16px; font-weight: 600; } +.developer-name { font-size: 18px; font-weight: 600; margin-bottom: 12px; } +.link-row { display: flex; align-items: center; padding: 12px 0; border-top: 1px solid rgba(0,0,0,0.1); } +.link-icon { font-size: 14px; font-weight: 600; color: #666; width: 70px; } +.link-value { flex: 1; font-size: 14px; color: #0084ff; text-decoration: none; } +.link-value:hover { text-decoration: underline; } +.link-arrow { font-size: 16px; color: #ccc; } +.bitcoin-row { display: flex; align-items: center; background: rgba(247, 147, 26, 0.08); border-radius: 12px; padding: 14px; gap: 10px; } +.bitcoin-label { font-size: 18px; font-weight: 600; color: #f7931a; } +.bitcoin-address { flex: 1; font-size: 11px; font-family: monospace; color: #666; word-break: break-all; } +.copy-btn { font-size: 12px; color: #999; cursor: pointer; min-width: 50px; text-align: right; } +.copy-btn:hover { color: #0084ff; } +.footer { text-align: center; margin-top: 32px; padding-top: 20px; border-top: 1px solid #ddd; } +.copyright { font-size: 12px; color: #999; } +.content h1 { font-size: 24px; margin-bottom: 16px; } +.content h2 { font-size: 18px; margin: 24px 0 12px; } +.content p { margin-bottom: 12px; } +.content ul { margin: 12px 0; padding-left: 24px; } +.content li { margin-bottom: 8px; } +` diff --git a/web/src/types.ts b/web/src/types.ts new file mode 100644 index 0000000..6419477 --- /dev/null +++ b/web/src/types.ts @@ -0,0 +1,18 @@ +export interface SiteConfig { + id: string + name: string + domain: string + scheme: string + version: string + category: string + description: string + backLink: string + icon: string + os: string + price: string +} + +export interface PageMeta { + title: string + template?: 'default' | 'app' | 'legal' +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..84fadc1 --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "strict": true, + "resolveJsonModule": true, + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "skipLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +}