ai/at
1
0
This commit is contained in:
2026-01-21 23:23:20 +09:00
parent 5288f33029
commit 182442e354
33 changed files with 631 additions and 634 deletions

View File

@@ -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'

4
.gitignore vendored
View File

@@ -4,4 +4,6 @@ deploy.yml
claude.md
embedded.mobileprovision
.env
html.zip
web/dist
node_modules
package-lock.json

View File

@@ -1,135 +0,0 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>App Info - Aiat</title>
<link rel="icon" type="image/png" href="/static/favicon.png">
<style>
* { 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; }
</style>
</head>
<body>
<div class="header">
<a href="/" class="back-link">&larr; Back to syu.is</a>
</div>
<div class="app-header">
<img src="/static/app.png" alt="Aiat" class="app-icon">
<div class="app-name">Aiat</div>
<div class="app-version">v1.111.0</div>
</div>
<div class="section">
<p class="description">Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.</p>
</div>
<div class="section">
<div class="section-title">App Information</div>
<div class="info-grid">
<div class="info-item">
<div class="info-label">Version</div>
<div class="info-value">1.111.0</div>
</div>
<div class="info-item">
<div class="info-label">Category</div>
<div class="info-value">Social</div>
</div>
<div class="info-item">
<div class="info-label">Supported OS</div>
<div class="info-value">iOS 26.0+</div>
</div>
<div class="info-item">
<div class="info-label">Price</div>
<div class="info-value">Free</div>
</div>
</div>
</div>
<div class="section">
<div class="section-title">Developer</div>
<div class="developer-name">syui</div>
<div class="link-row">
<span class="link-icon">Git</span>
<a href="https://git.syui.ai/syui" class="link-value" target="_blank">git.syui.ai/syui</a>
<span class="link-arrow">&rarr;</span>
</div>
<div class="link-row">
<span class="link-icon">ATProto</span>
<a href="https://syu.is/syui" class="link-value" target="_blank">syu.is/syui</a>
<span class="link-arrow">&rarr;</span>
</div>
</div>
<div class="section">
<div class="section-title">Bitcoin</div>
<div class="bitcoin-row">
<span class="bitcoin-label">&#8383;</span>
<span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span>
<span class="copy-btn" onclick="copyBTC()">copy</span>
</div>
</div>
<div class="footer">
<p class="copyright">&copy; syui</p>
</div>
<script>
function copyBTC() {
const addr = document.getElementById('btc-address').textContent;
navigator.clipboard.writeText(addr).then(() => {
const btn = document.querySelector('.copy-btn');
btn.textContent = 'copied!';
btn.style.color = '#4CAF50';
setTimeout(() => {
btn.textContent = 'copy';
btn.style.color = '';
}, 2000);
});
}
</script>
</body>
</html>

View File

@@ -1,100 +0,0 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>Help - syu.is</title>
<link rel="icon" type="image/png" href="/static/favicon.png">
<style>
* { 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; }
}
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
h2 { font-size: 20px; margin: 24px 0 12px; }
h3 { font-size: 16px; margin: 16px 0 8px; }
p { margin-bottom: 16px; }
ul { margin: 0 0 16px 24px; }
li { margin-bottom: 8px; }
a { color: #0066cc; text-decoration: none; }
a:hover { text-decoration: underline; }
.header { margin-bottom: 32px; }
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
.faq-item { margin-bottom: 24px; }
.contact-box { background: #f5f5f5; padding: 20px; border-radius: 8px; margin: 20px 0; }
@media (prefers-color-scheme: dark) {
.contact-box { background: #1a1a1a; }
}
</style>
</head>
<body>
<div class="header">
<a href="/" class="back-link">&larr; Back to syu.is</a>
<h1>Help Center</h1>
</div>
<h2>About syu.is</h2>
<p>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.</p>
<h2>Frequently Asked Questions</h2>
<div class="faq-item">
<h3>What is the AT Protocol?</h3>
<p>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.</p>
</div>
<div class="faq-item">
<h3>How do I create an account?</h3>
<p>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.</p>
</div>
<div class="faq-item">
<h3>How do I reset my password?</h3>
<p>You can reset your password through the login screen by selecting "Forgot Password" and following the instructions sent to your email.</p>
</div>
<div class="faq-item">
<h3>How do I delete my account?</h3>
<p>You can delete your account through Settings &gt; Account. Please note that account deletion is permanent and cannot be undone.</p>
</div>
<div class="faq-item">
<h3>How do I report abuse or inappropriate content?</h3>
<p>You can report content by using the report function available on each post. Our moderation team will review reports and take appropriate action.</p>
</div>
<h2>Contact</h2>
<div class="contact-box">
<p>For additional support or questions:</p>
<ul>
<li>GitHub: <a href="https://github.com/syui" target="_blank">github.com/syui</a></li>
</ul>
</div>
<h2>Related Links</h2>
<ul>
<li><a href="/about/support/tos">Terms of Service</a></li>
<li><a href="/about/support/privacy-policy">Privacy Policy</a></li>
<li><a href="/about/support/license">License</a></li>
<li><a href="/about/support/app">App Info</a></li>
<li><a href="https://atproto.com" target="_blank">AT Protocol Documentation</a></li>
</ul>
<div class="footer">
<p>Last updated: 2025</p>
<p>&copy; syui</p>
</div>
</body>
</html>

View File

@@ -1,66 +0,0 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>License - syu.is</title>
<link rel="icon" type="image/png" href="/static/favicon.png">
<style>
* { 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; }
}
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
h2 { font-size: 20px; margin: 24px 0 12px; }
p { margin-bottom: 16px; }
ul { margin: 0 0 16px 24px; }
li { margin-bottom: 8px; }
a { color: #0066cc; text-decoration: none; }
a:hover { text-decoration: underline; }
.header { margin-bottom: 32px; }
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
pre { background: #f5f5f5; padding: 16px; border-radius: 8px; overflow-x: auto; font-size: 13px; }
@media (prefers-color-scheme: dark) { pre { background: #1a1a1a; } }
</style>
</head>
<body>
<div class="header">
<a href="/" class="back-link">&larr; Back to syu.is</a>
<h1>License</h1>
</div>
<h2>Aiat (iOS/Android App)</h2>
<p>This application is based on the Bluesky Social App, which is open source software.</p>
<h2>Open Source Licenses</h2>
<p>This app uses the following open source software:</p>
<h3>Bluesky Social App</h3>
<p>Licensed under the MIT License</p>
<p><a href="https://github.com/bluesky-social/social-app" target="_blank">https://github.com/bluesky-social/social-app</a></p>
<h3>AT Protocol</h3>
<p>Licensed under the MIT License / Apache 2.0</p>
<p><a href="https://github.com/bluesky-social/atproto" target="_blank">https://github.com/bluesky-social/atproto</a></p>
<h2>Third Party Libraries</h2>
<p>This application includes various third-party libraries, each with their own licenses. For a complete list, please see the application's source code repository.</p>
<div class="footer">
<p>Last updated: 2025</p>
<p>&copy; syui</p>
</div>
</body>
</html>

View File

@@ -1,92 +0,0 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>Privacy Policy - syu.is</title>
<link rel="icon" type="image/png" href="/static/favicon.png">
<style>
* { 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; }
}
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
h2 { font-size: 20px; margin: 24px 0 12px; }
p { margin-bottom: 16px; }
ul { margin: 0 0 16px 24px; }
li { margin-bottom: 8px; }
a { color: #0066cc; text-decoration: none; }
a:hover { text-decoration: underline; }
.header { margin-bottom: 32px; }
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
</style>
</head>
<body>
<div class="header">
<a href="/" class="back-link">&larr; Back to syu.is</a>
<h1>Privacy Policy</h1>
</div>
<h2>1. Introduction</h2>
<p>This Privacy Policy explains how syu.is collects, uses, and protects your personal information when you use our service.</p>
<h2>2. Information We Collect</h2>
<p>We collect the following types of information:</p>
<ul>
<li><strong>Account Information:</strong> Email address, username, and profile information you provide</li>
<li><strong>Content:</strong> Posts, messages, and other content you create on the platform</li>
<li><strong>Usage Data:</strong> Information about how you interact with our service</li>
<li><strong>Device Information:</strong> Browser type, operating system, and device identifiers</li>
</ul>
<h2>3. How We Use Your Information</h2>
<p>We use your information to:</p>
<ul>
<li>Provide and maintain our service</li>
<li>Improve and personalize your experience</li>
<li>Communicate with you about the service</li>
<li>Ensure security and prevent abuse</li>
</ul>
<h2>4. Data Sharing</h2>
<p>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.</p>
<h2>5. Data Security</h2>
<p>We implement appropriate security measures to protect your personal information. However, no method of transmission over the Internet is 100% secure.</p>
<h2>6. Your Rights</h2>
<p>You have the right to:</p>
<ul>
<li>Access your personal data</li>
<li>Request correction of your data</li>
<li>Request deletion of your account</li>
<li>Export your data</li>
</ul>
<h2>7. Cookies</h2>
<p>We use cookies and similar technologies to maintain your session and improve your experience.</p>
<h2>8. Changes to This Policy</h2>
<p>We may update this Privacy Policy from time to time. We will notify you of any significant changes.</p>
<h2>9. Contact</h2>
<p>For privacy-related questions, please visit our <a href="/about/support/help">Help page</a>.</p>
<div class="footer">
<p>Last updated: 2025</p>
<p>&copy; syui</p>
</div>
</body>
</html>

View File

@@ -1,84 +0,0 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>Terms of Service - syu.is</title>
<link rel="icon" type="image/png" href="/static/favicon.png">
<style>
* { 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; }
}
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
h2 { font-size: 20px; margin: 24px 0 12px; }
p { margin-bottom: 16px; }
ul { margin: 0 0 16px 24px; }
li { margin-bottom: 8px; }
a { color: #0066cc; text-decoration: none; }
a:hover { text-decoration: underline; }
.header { margin-bottom: 32px; }
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
</style>
</head>
<body>
<div class="header">
<a href="/" class="back-link">&larr; Back to syu.is</a>
<h1>Terms of Service</h1>
</div>
<h2>1. Introduction</h2>
<p>Welcome to syu.is. By using our service, you agree to these terms. Please read them carefully.</p>
<h2>2. Service Description</h2>
<p>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.</p>
<h2>3. User Responsibilities</h2>
<p>As a user of syu.is, you agree to:</p>
<ul>
<li>Provide accurate information when creating an account</li>
<li>Keep your account credentials secure</li>
<li>Not use the service for illegal activities</li>
<li>Respect other users and their content</li>
<li>Comply with applicable laws and regulations</li>
</ul>
<h2>4. Content Guidelines</h2>
<p>Users are responsible for the content they post. Prohibited content includes:</p>
<ul>
<li>Illegal content</li>
<li>Harassment or abuse</li>
<li>Spam or misleading information</li>
<li>Content that violates others' rights</li>
</ul>
<h2>5. Privacy</h2>
<p>Your privacy is important to us. Please review our <a href="/about/support/privacy-policy">Privacy Policy</a> to understand how we handle your data.</p>
<h2>6. Disclaimer</h2>
<p>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.</p>
<h2>7. Changes to Terms</h2>
<p>We may update these terms from time to time. Continued use of the service after changes constitutes acceptance of the new terms.</p>
<h2>8. Contact</h2>
<p>For questions about these terms, please visit our <a href="/about/support/help">Help page</a>.</p>
<div class="footer">
<p>Last updated: 2025</p>
<p>&copy; syui</p>
</div>
</body>
</html>

View File

@@ -1,135 +0,0 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>App Info - Aiat</title>
<link rel="icon" type="image/png" href="/static/favicon.png">
<style>
* { 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; }
</style>
</head>
<body>
<div class="header">
<a href="/" class="back-link">&larr; Back to syu.is</a>
</div>
<div class="app-header">
<img src="/static/app.png" alt="Aiat" class="app-icon">
<div class="app-name">Aiat</div>
<div class="app-version">v1.111.0</div>
</div>
<div class="section">
<p class="description">Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.</p>
</div>
<div class="section">
<div class="section-title">App Information</div>
<div class="info-grid">
<div class="info-item">
<div class="info-label">Version</div>
<div class="info-value">1.111.2</div>
</div>
<div class="info-item">
<div class="info-label">Category</div>
<div class="info-value">Social</div>
</div>
<div class="info-item">
<div class="info-label">Supported OS</div>
<div class="info-value">iOS 26.0+</div>
</div>
<div class="info-item">
<div class="info-label">Price</div>
<div class="info-value">Free</div>
</div>
</div>
</div>
<div class="section">
<div class="section-title">Developer</div>
<div class="developer-name">syui</div>
<div class="link-row">
<span class="link-icon">Git</span>
<a href="https://git.syui.ai/syui" class="link-value" target="_blank">git.syui.ai/syui</a>
<span class="link-arrow">&rarr;</span>
</div>
<div class="link-row">
<span class="link-icon">ATProto</span>
<a href="https://syu.is/syui" class="link-value" target="_blank">syu.is/syui</a>
<span class="link-arrow">&rarr;</span>
</div>
</div>
<div class="section">
<div class="section-title">Bitcoin</div>
<div class="bitcoin-row">
<span class="bitcoin-label">&#8383;</span>
<span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span>
<span class="copy-btn" onclick="copyBTC()">copy</span>
</div>
</div>
<div class="footer">
<p class="copyright">&copy; syui</p>
</div>
<script>
function copyBTC() {
const addr = document.getElementById('btc-address').textContent;
navigator.clipboard.writeText(addr).then(() => {
const btn = document.querySelector('.copy-btn');
btn.textContent = 'copied!';
btn.style.color = '#4CAF50';
setTimeout(() => {
btn.textContent = 'copy';
btn.style.color = '';
}, 2000);
});
}
</script>
</body>
</html>

View File

@@ -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

28
web/package.json Normal file
View File

@@ -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"
}
}

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

Before

Width:  |  Height:  |  Size: 1005 B

After

Width:  |  Height:  |  Size: 1005 B

124
web/src/build.ts Normal file
View File

@@ -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<typeof loadPages>): 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()

View File

@@ -0,0 +1,5 @@
---
title: App Info
template: app
path: app/index.html
---

View File

@@ -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.

View File

@@ -0,0 +1,5 @@
---
title: App Info
template: app
path: index.html
---

View File

@@ -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.

View File

@@ -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*

View File

@@ -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*

13
web/src/sites/aiat.json Normal file
View File

@@ -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"
}

13
web/src/sites/aicard.json Normal file
View File

@@ -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"
}

13
web/src/sites/airse.json Normal file
View File

@@ -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"
}

View File

@@ -0,0 +1,3 @@
export { layout, appLayout, legalLayout } from './layout.js'
export { oauthCallback, clientMetadata } from './oauth.js'
export { baseStyles } from './styles.js'

105
web/src/templates/layout.ts Normal file
View File

@@ -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 `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>${title} - ${site.name}</title>
<link rel="icon" type="image/png" href="/static/favicon.png">
<style>${baseStyles}</style>
</head>
<body>
<div class="header">
<a href="/" class="back-link">&larr; Back to ${site.backLink}</a>
</div>
${content}
<div class="footer">
<p class="copyright">&copy; syui</p>
</div>
</body>
</html>`
}
export function appLayout(site: SiteConfig, content: string): string {
return layout(site, 'App Info', `
<div class="app-header">
<img src="${site.icon}" alt="${site.name}" class="app-icon">
<div class="app-name">${site.name}</div>
</div>
<div class="section">
<p class="description">${site.description}</p>
</div>
<div class="section">
<div class="section-title">App Information</div>
<div class="info-grid">
<div class="info-item">
<div class="info-label">Category</div>
<div class="info-value">${site.category}</div>
</div>
<div class="info-item">
<div class="info-label">Supported OS</div>
<div class="info-value">${site.os}</div>
</div>
<div class="info-item">
<div class="info-label">Price</div>
<div class="info-value">${site.price}</div>
</div>
</div>
</div>
<div class="section">
<div class="section-title">Developer</div>
<div class="developer-name">syui</div>
<div class="link-row">
<span class="link-icon">Git</span>
<a href="https://git.syui.ai/syui" class="link-value" target="_blank">git.syui.ai/syui</a>
<span class="link-arrow">&rarr;</span>
</div>
<div class="link-row">
<span class="link-icon">ATProto</span>
<a href="https://syui.ai" class="link-value" target="_blank">syui.ai</a>
<span class="link-arrow">&rarr;</span>
</div>
</div>
<div class="section">
<div class="section-title">Bitcoin</div>
<div class="bitcoin-row">
<span class="bitcoin-label">&#8383;</span>
<span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span>
<span class="copy-btn" onclick="copyBTC()">copy</span>
</div>
</div>
${content}
<script>
function copyBTC() {
const addr = document.getElementById('btc-address').textContent;
navigator.clipboard.writeText(addr).then(() => {
const btn = document.querySelector('.copy-btn');
btn.textContent = 'copied!';
btn.style.color = '#4CAF50';
setTimeout(() => {
btn.textContent = 'copy';
btn.style.color = '';
}, 2000);
});
}
</script>
`)
}
export function legalLayout(site: SiteConfig, title: string, content: string): string {
return layout(site, title, `
<div class="content">
<h1>${title}</h1>
${content}
</div>
`)
}

View File

@@ -0,0 +1,46 @@
import type { SiteConfig } from '../types.js'
export function oauthCallback(site: SiteConfig): string {
return `<!DOCTYPE html>
<html>
<head>
<title>Redirecting...</title>
<script>
const params = window.location.search;
window.location.href = '${site.scheme}://oauth/callback' + params;
setTimeout(function () {
window.location.replace('/?oauth_callback=true' + params.replace('?', '&'));
}, 500);
</script>
</head>
<body>
<p>Redirecting to app...</p>
<p>If nothing happens, <a href="#"
onclick="window.location.replace('/?oauth_callback=true' + window.location.search.replace('?', '&')); return false;">click
here</a> to continue in browser.</p>
</body>
</html>`
}
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)
}

View File

@@ -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; }
`

18
web/src/types.ts Normal file
View File

@@ -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'
}

16
web/tsconfig.json Normal file
View File

@@ -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"]
}