fix html
This commit is contained in:
3
web/src/templates/index.ts
Normal file
3
web/src/templates/index.ts
Normal 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
105
web/src/templates/layout.ts
Normal 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">← Back to ${site.backLink}</a>
|
||||
</div>
|
||||
${content}
|
||||
<div class="footer">
|
||||
<p class="copyright">© 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">→</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">→</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Bitcoin</div>
|
||||
<div class="bitcoin-row">
|
||||
<span class="bitcoin-label">₿</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>
|
||||
`)
|
||||
}
|
||||
46
web/src/templates/oauth.ts
Normal file
46
web/src/templates/oauth.ts
Normal 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)
|
||||
}
|
||||
51
web/src/templates/styles.ts
Normal file
51
web/src/templates/styles.ts
Normal 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; }
|
||||
`
|
||||
Reference in New Issue
Block a user