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
1 change: 1 addition & 0 deletions apps/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"check:render": "node scripts/render-check.mjs",
"check:deployed": "node scripts/deployed-check.mjs",
"check:aria": "node scripts/aria-refs-check.mjs",
"check:service-entry": "node scripts/service-entry-check.mjs",
"typecheck:update": "node scripts/typecheck-ratchet.mjs --update",
"audit:en": "node qa/lang_audit.mjs",
"audit:ru": "node qa/ru_audit.mjs"
Expand Down
26 changes: 26 additions & 0 deletions apps/website/scripts/service-entry-check.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { readFileSync } from 'node:fs'

const root = new URL('..', import.meta.url)
const component = readFileSync(new URL('src/components/ServiceEntry.tsx', root), 'utf8')
const app = readFileSync(new URL('src/App.tsx', root), 'utf8')

const requirements = [
['official Telegram bot URL', component.includes('https://t.me/t27ai_bot?start=website')],
['English Telegram CTA', component.includes('Open in Telegram')],
['Russian Telegram CTA', component.includes('Открыть в Telegram')],
['English verified-profile explanation', component.includes('verified Telegram profile')],
['Russian verified-profile explanation', component.includes('подтверждённого профиля Telegram')],
['browser CTA uses native disabled semantics', component.includes('<button type="button" className="btn secondary" disabled')],
['browser CTA has no URL constant', !component.includes('BROWSER_URL')],
['no embedded Mini App URL', !component.includes('startapp=')],
['safe external-link attributes', component.includes('rel="noopener noreferrer"')],
['homepage placement', app.includes('<ServiceEntry />')],
]

const failed = requirements.filter(([, ok]) => !ok)
if (failed.length) {
for (const [name] of failed) console.error(`FAIL: ${name}`)
process.exit(1)
}

console.log(`PASS: ${requirements.length} service-entry requirements`)
2 changes: 2 additions & 0 deletions apps/website/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { lazy, Suspense } from 'react'
import Navigation from './components/Navigation'
import Footer from './components/Footer'
import ServiceEntry from './components/ServiceEntry'
import { TnfHero } from './components/sections/tnf'

// Главная страница построена под статьёй «Trinity S³AI: Ternary Network Floats»
Expand Down Expand Up @@ -45,6 +46,7 @@ export default function App() {
<Navigation />

<TnfHero />
<ServiceEntry />

<Suspense fallback={<SectionFallback />}>
<TnfClaim />
Expand Down
57 changes: 57 additions & 0 deletions apps/website/src/components/ServiceEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useI18n } from '../i18n/context'

const TELEGRAM_URL = 'https://t.me/t27ai_bot?start=website'

const copy = {
en: {
eyebrow: 'TRINITY S3AI SERVICE',
title: 'Continue with your own Telegram identity',
body: 'The agent works only inside a verified Telegram profile. The website does not create a parallel account, expose credentials, or let one user act as another.',
telegram: 'Open in Telegram',
browser: 'Try in browser',
browserNote: 'Browser access will appear here after the owner confirms a stable public Mini App URL.',
},
ru: {
eyebrow: 'СЕРВИС TRINITY S3AI',
title: 'Продолжите со своим профилем Telegram',
body: 'Агент работает только внутри подтверждённого профиля Telegram. Сайт не создаёт параллельную учётную запись, не раскрывает ключи и не позволяет действовать от имени другого пользователя.',
telegram: 'Открыть в Telegram',
browser: 'Попробовать в браузере',
browserNote: 'Вход из браузера появится здесь после подтверждения владельцем стабильного публичного URL Mini App.',
},
}

export default function ServiceEntry() {
const { lang } = useI18n()
const t = copy[lang === 'ru' ? 'ru' : 'en']

return (
<section className="tnf-section" aria-labelledby="service-entry-title" style={{ paddingTop: 0 }}>
<div className="tnf-wrap">
<div className="tnf-cell" style={{ padding: 'clamp(var(--sp2), 5vw, var(--sp3))' }}>
<span className="tnf-badge" style={{ color: 'var(--accent)' }}>{t.eyebrow}</span>
<h2 id="service-entry-title" className="tnf-h2" style={{ marginTop: 'var(--sp0)' }}>{t.title}</h2>
<p className="tnf-lede" style={{ marginBottom: 'var(--sp2)' }}>{t.body}</p>

<div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--sp0)', alignItems: 'center' }}>
<a
className="btn"
href={TELEGRAM_URL}
target="_blank"
rel="noopener noreferrer"
aria-label={`${t.telegram} — @t27ai_bot`}
>
{t.telegram}
</a>
<button type="button" className="btn secondary" disabled style={{ cursor: 'not-allowed', opacity: 0.55 }}>
{t.browser}
</button>
</div>
<p style={{ color: 'var(--muted)', fontSize: 'var(--f-1)', margin: 'var(--sp0) 0 0', maxWidth: '68ch' }}>
{t.browserNote}
</p>
</div>
</div>
</section>
)
}
Loading