Initial commit (fresh repo)

This commit is contained in:
Maxim
2025-12-12 02:57:18 +03:00
commit cc4809019d
24 changed files with 17258 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

30
AGENTS.md Normal file
View File

@@ -0,0 +1,30 @@
# Repository Guidelines
## Project Structure & Module Organization
- `app/app.vue` hosts the root Nuxt view; keep it lean and delegate features to components under a future `components/` directory.
- `public/` holds static assets served as-is.
- `nuxt.config.ts` centralizes Nuxt settings; add modules and runtime config here instead of inside components.
- `package.json` scripts are the source of truth for commands; avoid ad-hoc shell scripts.
## Build, Test, and Development Commands
- `npm run dev` — start Nuxt dev server at `http://localhost:3000` with HMR.
- `npm run build` — production build; ensure this passes before merging.
- `npm run preview` — serve the production build locally to validate routes and assets.
- `npm run generate` — create a static export for static hosting targets.
- Install dependencies with `npm install` (npm lockfile is committed).
## Coding Style & Naming Conventions
- Language: Vue 3 with Nuxt 4; prefer `<script setup lang="ts">` and the Composition API.
- Formatting: 2-space indentation; run a formatter (e.g., `npx prettier`) before pushing; keep templates tidy and avoid inline styles when practical.
- Naming: PascalCase for components (`HeroSection.vue`), camelCase for composables/utilities (`useFeatureFlag`), kebab-case for file names except Vue components.
- Imports: use Nuxt aliases for clarity (e.g., `~/components/Button.vue`) instead of long relative paths.
## Testing Guidelines
- No automated tests are configured yet; add coverage with new features rather than deferring.
- If you add tests, co-locate unit specs with components (`Button.spec.ts`) and keep integration/E2E flows under `tests/`.
- Always run `npm run build` and do a quick browser smoke test of impacted routes before opening a PR.
## Commit & Pull Request Guidelines
- Commits: imperative mood (`Add hero CTA copy`) and scoped to a single concern; avoid mixing refactors with feature work.
- Pull Requests: include a concise summary, screenshots or recordings for UI changes, reproduction steps for fixes, and any follow-up items.
- Link issues when relevant and call out breaking changes explicitly in the description.

78
README.md Normal file
View File

@@ -0,0 +1,78 @@
# Nuxt Minimal Starter
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm preview
# yarn
yarn preview
# bun
bun run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
Для production: установи переменную окружения:
NUXT_PUBLIC_API_BASE=https://your-api-domain.com

5
app/app.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>

View File

@@ -0,0 +1,70 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
color-scheme: light;
--font-heading: 'Space Grotesk', 'Manrope', system-ui, -apple-system, sans-serif;
--font-body: 'Manrope', system-ui, -apple-system, sans-serif;
}
body {
@apply bg-slate-50 text-slate-900 font-body antialiased;
}
@layer base {
.font-heading {
font-family: var(--font-heading);
}
.font-body {
font-family: var(--font-body);
}
}
@layer utilities {
.shadow-card {
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.12);
}
.shadow-floating {
box-shadow: 0 14px 42px rgba(59, 130, 246, 0.18);
}
}
.gradient-ring {
background: radial-gradient(circle at 20% 20%, rgba(59, 130, 246, 0.12), transparent 35%),
radial-gradient(circle at 80% 10%, rgba(14, 165, 233, 0.12), transparent 32%),
radial-gradient(circle at 50% 80%, rgba(99, 102, 241, 0.1), transparent 30%);
}
.section-shell {
@apply relative overflow-hidden rounded-3xl border border-slate-200 bg-white/95 backdrop-blur shadow-card;
}
.card-surface {
@apply rounded-2xl border border-slate-200 bg-white p-6 shadow-card transition duration-300;
}
.card-surface:hover {
transform: translateY(-0.25rem);
box-shadow: 0 12px 34px rgba(59, 130, 246, 0.16);
}
.badge {
@apply inline-flex items-center gap-2 rounded-full bg-blue-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-blue-700;
}
.fade-up {
opacity: 0;
animation: fade-up 0.9s ease forwards;
}
@keyframes fade-up {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

35
app/components/CTA.vue Normal file
View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
const config = useRuntimeConfig()
function trackBot() {
fetch(`${config.public.apiBase}/api/track/bot/`, { method: 'POST' }).catch(() => {})
}
</script>
<template>
<section class="py-16 sm:py-20 lg:py-24">
<div class="container mx-auto max-w-5xl px-4">
<div class="section-shell p-[1px] bg-gradient-to-r from-blue-100 via-white to-indigo-100">
<div class="rounded-3xl bg-white px-8 py-10 text-center shadow-card sm:px-12 sm:py-14">
<p class="badge mx-auto">Готов к старту</p>
<h2 class="mt-4 font-heading text-3xl sm:text-4xl">Начни изучать язык сегодня</h2>
<p class="mt-3 text-lg text-slate-700 sm:text-xl">Учись в удобном формате с помощью AI</p>
<div class="mt-6 flex flex-wrap items-center justify-center gap-4">
<a
class="inline-flex items-center gap-2 rounded-full bg-blue-600 px-6 py-3 text-base font-semibold text-white shadow-floating transition hover:-translate-y-1 hover:bg-blue-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
href="https://t.me/animeenigma_language_bot"
target="_blank"
rel="noreferrer"
@click="trackBot"
>
Открыть Telegram-бота
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5 12h14m0 0-5-5m5 5-5 5" />
</svg>
</a>
</div>
</div>
</div>
</div>
</section>
</template>

View File

@@ -0,0 +1,52 @@
<template>
<section class="py-14 sm:py-16 lg:py-20">
<div class="container mx-auto max-w-6xl px-4">
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div>
<p class="badge">Диалоговая практика</p>
<h2 class="font-heading text-2xl sm:text-3xl lg:text-4xl">6 сценариев с AI-собеседником</h2>
<p class="mt-2 text-slate-600">AI ведёт разговор, исправляет ошибки, даёт подсказки и перевод сообщений.</p>
</div>
<div class="text-sm text-slate-500">Карточки анимируются при наведении.</div>
</div>
<div class="mt-8 grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div
v-for="(item, idx) in dialogs"
:key="item.title"
class="card-surface fade-up"
:style="{ animationDelay: `${idx * 0.07}s` }"
>
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="flex h-10 w-10 items-center justify-center rounded-full bg-blue-50 text-lg">{{ item.emoji }}</div>
<div>
<p class="text-sm text-slate-500">{{ item.label }}</p>
<h3 class="font-heading text-lg">{{ item.title }}</h3>
</div>
</div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 5l7 7-7 7" />
</svg>
</div>
<p class="mt-3 text-sm text-slate-600">{{ item.desc }}</p>
<div class="mt-4 flex flex-wrap gap-2 text-xs text-slate-700">
<span class="rounded-full bg-blue-50 px-3 py-1 text-blue-800">Исправление ошибок</span>
<span class="rounded-full bg-indigo-50 px-3 py-1 text-indigo-800">Подсказки</span>
<span class="rounded-full bg-emerald-50 px-3 py-1 text-emerald-800">Перевод</span>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
const dialogs = [
{ emoji: '🍣', label: 'Ресторан', title: 'Бронь и заказ', desc: 'Практикуй заказ блюд, замену ингредиентов и small talk с официантом.' },
{ emoji: '🛍️', label: 'Магазин', title: 'Покупки и возврат', desc: 'Спрашивай про размеры, оформляй возвраты и уточняй акции.' },
{ emoji: '✈️', label: 'Путешествия', title: 'Аэропорт и отель', desc: 'Check-in, паспортный контроль, просьбы на стойке регистрации.' },
{ emoji: '💼', label: 'Работа', title: 'Созвоны и письма', desc: 'Стэнд-апы, уточнение задач, переговоры с заказчиком.' },
{ emoji: '⚕️', label: 'Врач', title: 'Запись и симптомы', desc: 'Опиши симптомы, получи рекомендации и отработай мед. лексику.' },
{ emoji: '💬', label: 'Свободный диалог', title: 'Топики по запросу', desc: 'AI поддержит разговор на любую тему и подскажет формулировки.' }
]
</script>

View File

@@ -0,0 +1,39 @@
<template>
<section class="py-14 sm:py-16 lg:py-20">
<div class="container mx-auto max-w-6xl px-4">
<div class="mb-10 flex flex-col gap-3 sm:mb-12 sm:flex-row sm:items-end sm:justify-between">
<div>
<p class="badge">Почему это удобно</p>
<h2 class="font-heading text-2xl sm:text-3xl lg:text-4xl">Преимущества бота</h2>
<p class="mt-2 text-slate-600">AI помогает учиться в любом чате Telegram, без лишних приложений.</p>
</div>
<div class="text-sm text-slate-500">Карточки реагируют на hover и подчеркивают ключевые функции.</div>
</div>
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<div
v-for="(item, idx) in items"
:key="item.title"
class="card-surface fade-up"
:style="{ animationDelay: `${idx * 0.06}s` }"
>
<div class="flex items-center gap-3">
<div class="flex h-11 w-11 items-center justify-center rounded-full bg-blue-50 text-lg">{{ item.emoji }}</div>
<h3 class="font-heading text-lg">{{ item.title }}</h3>
</div>
<p class="mt-3 text-sm text-slate-600">{{ item.desc }}</p>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
const items = [
{ emoji: '🤖', title: 'AI внутри', desc: 'Умная проверка ответов, генерация заданий и диалоговая практика на лету.' },
{ emoji: '📱', title: 'Всегда под рукой', desc: 'Учись прямо в Telegram на телефоне или десктопе — ничего не нужно устанавливать.' },
{ emoji: '🎯', title: 'Персональный подход', desc: 'Задания и диалоги адаптируются под уровень A1C2 и N5N1.' },
{ emoji: '📚', title: 'Контекст важен', desc: 'Слова сохраняются вместе с примерами, чтобы запоминать в реальных ситуациях.' },
{ emoji: '🆓', title: 'Бесплатно', desc: 'Основной функционал доступен без оплаты — достаточно открыть бота.' },
{ emoji: '⚡', title: 'Быстрый старт', desc: 'Первые задания за минуту: бот сразу подбирает нужный сценарий.' }
]
</script>

37
app/components/Footer.vue Normal file
View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
const config = useRuntimeConfig()
function trackBot() {
fetch(`${config.public.apiBase}/api/track/bot/`, { method: 'POST' }).catch(() => {})
}
function trackChannel() {
fetch(`${config.public.apiBase}/api/track/channel/`, { method: 'POST' }).catch(() => {})
}
</script>
<template>
<footer class="border-t border-slate-200 bg-white py-10 text-slate-700">
<div class="container mx-auto max-w-6xl px-4">
<div class="flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between">
<div class="space-y-2">
<div class="flex items-center gap-2 text-lg font-heading text-slate-900">
<span class="flex h-9 w-9 items-center justify-center rounded-full bg-blue-600 text-white font-bold">AE</span>
Anime Enigma Language Bot
</div>
<p class="text-sm text-slate-600">AI-помощник для английского A1C2 и японского N5N1.</p>
</div>
<div class="flex flex-wrap items-center gap-4 text-sm">
<a class="inline-flex items-center gap-2 hover:text-blue-700" href="https://t.me/animeenigma_language_bot" target="_blank" rel="noreferrer" @click="trackBot">
<span class="h-2 w-2 rounded-full bg-emerald-500"></span>
@animeenigma_language_bot
</a>
<a class="inline-flex items-center gap-2 hover:text-blue-700" href="https://t.me/animeenigmalanguage" target="_blank" rel="noreferrer" @click="trackChannel">
<span class="h-2 w-2 rounded-full bg-cyan-500"></span>
@animeenigmalanguage
</a>
</div>
</div>
</div>
</footer>
</template>

109
app/components/Hero.vue Normal file
View File

@@ -0,0 +1,109 @@
<script setup lang="ts">
const config = useRuntimeConfig()
function trackBot() {
fetch(`${config.public.apiBase}/api/track/bot/`, { method: 'POST' }).catch(() => {})
}
function trackChannel() {
fetch(`${config.public.apiBase}/api/track/channel/`, { method: 'POST' }).catch(() => {})
}
</script>
<template>
<section class="relative overflow-hidden py-16 sm:py-20 lg:py-24">
<div class="container mx-auto max-w-6xl px-4">
<div class="section-shell grid items-center gap-12 p-8 sm:p-10 lg:grid-cols-2">
<div class="space-y-6 fade-up">
<div class="badge">AI + Telegram</div>
<h1 class="font-heading text-3xl leading-tight sm:text-4xl lg:text-5xl">
Telegram-бот для изучения английского и японского с AI
</h1>
<p class="text-lg text-slate-600 sm:text-xl">
Пополняй словарь, выполняй задания, практикуй диалоги всё в одном боте. Искусственный интеллект адаптируется
под твой уровень.
</p>
<div class="flex flex-wrap gap-4">
<a
class="inline-flex items-center gap-2 rounded-full bg-blue-600 px-6 py-3 text-base font-semibold text-white shadow-md transition hover:-translate-y-0.5 hover:bg-blue-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
href="https://t.me/animeenigma_language_bot"
target="_blank"
rel="noreferrer"
@click="trackBot"
>
Открыть бота
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5 12h14m0 0-5-5m5 5-5 5" />
</svg>
</a>
<a
class="inline-flex items-center gap-2 rounded-full border border-slate-300 bg-white px-6 py-3 text-base font-semibold text-slate-900 shadow-sm transition hover:-translate-y-0.5 hover:bg-slate-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
href="https://t.me/animeenigmalanguage"
target="_blank"
rel="noreferrer"
@click="trackChannel"
>
Telegram-канал
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M14.25 9.75 16.5 12l-2.25 2.25m-3-4.5L9 12l2.25 2.25" />
</svg>
</a>
</div>
<div class="flex flex-wrap gap-3 text-sm text-slate-500">
<span class="inline-flex items-center gap-2 rounded-full bg-blue-50 px-3 py-1 text-blue-800">
<span class="h-2 w-2 rounded-full bg-green-500"></span>
Уровни A1C2 и N5N1
</span>
<span class="inline-flex items-center gap-2 rounded-full bg-indigo-50 px-3 py-1 text-indigo-800">
<span class="h-2 w-2 rounded-full bg-cyan-500"></span>
Быстрый старт без установки
</span>
</div>
</div>
<div class="relative isolate">
<div class="absolute -left-10 -top-14 h-64 w-64 rounded-full bg-blue-200 opacity-60 blur-3xl"></div>
<div class="absolute -right-16 bottom-6 h-52 w-52 rounded-full bg-gradient-to-br from-indigo-200 to-blue-100 blur-3xl"></div>
<div class="relative grid gap-4">
<div class="card-surface fade-up">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-slate-500">Уровень</p>
<p class="text-2xl font-semibold">CEFR B2 · JLPT N3</p>
</div>
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-blue-50 text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6v6l4 2" />
</svg>
</div>
</div>
<div class="mt-4 h-2 w-full rounded-full bg-slate-100">
<div class="h-2 w-3/4 rounded-full bg-blue-600"></div>
</div>
<p class="mt-3 text-sm text-slate-600">AI подбирает задания под оба языка одновременно.</p>
</div>
<div class="card-surface fade-up" style="animation-delay: 0.15s">
<div class="flex items-center gap-4">
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-indigo-50 text-indigo-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 11.25c1.864 0 3.375-1.511 3.375-3.375S13.864 4.5 12 4.5s-3.375 1.511-3.375 3.375S10.136 11.25 12 11.25Z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5.25 20.25a6.75 6.75 0 1 1 13.5 0" />
</svg>
</div>
<div>
<p class="text-sm text-slate-500">Диалоги</p>
<p class="text-lg font-semibold text-slate-900">AI-собеседник и мгновенные подсказки</p>
</div>
</div>
<div class="mt-4 grid grid-cols-3 gap-2 text-xs text-slate-700">
<span class="rounded-full bg-blue-50 px-3 py-2 text-center text-blue-800">Ресторан</span>
<span class="rounded-full bg-indigo-50 px-3 py-2 text-center text-indigo-800">Путешествия</span>
<span class="rounded-full bg-slate-100 px-3 py-2 text-center text-slate-800">Работа</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>

View File

@@ -0,0 +1,48 @@
<template>
<section class="py-14 sm:py-16 lg:py-20">
<div class="container mx-auto max-w-6xl px-4">
<div class="section-shell space-y-8 p-8 sm:p-10">
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div>
<p class="badge">Поддерживаемые языки</p>
<h2 class="font-heading text-2xl sm:text-3xl lg:text-4xl">Английский и японский</h2>
<p class="mt-2 text-slate-600">Уровни CEFR A1C2 и JLPT N5N1, интерфейс на русском, английском и японском.</p>
</div>
<div class="rounded-full border border-slate-200 bg-slate-50 px-4 py-2 text-sm text-slate-700">Интерфейс: RU · EN · JP</div>
</div>
<div class="grid gap-6 md:grid-cols-2">
<div class="card-surface fade-up">
<div class="flex items-center gap-3">
<span class="text-2xl">🇬🇧</span>
<div>
<p class="text-sm uppercase tracking-wide text-slate-500">English</p>
<h3 class="font-heading text-xl">CEFR A1C2</h3>
</div>
</div>
<p class="mt-3 text-sm text-slate-600">Диалоги, задания и словарь с транскрипциями, устойчивыми выражениями и примерами.</p>
<div class="mt-4 flex flex-wrap gap-2 text-xs text-slate-700">
<span class="rounded-full bg-blue-50 px-3 py-1 text-blue-800">Произношение</span>
<span class="rounded-full bg-indigo-50 px-3 py-1 text-indigo-800">Фразовые глаголы</span>
<span class="rounded-full bg-amber-50 px-3 py-1 text-amber-800">Бизнес-лексика</span>
</div>
</div>
<div class="card-surface fade-up" style="animation-delay: 0.08s">
<div class="flex items-center gap-3">
<span class="text-2xl">🇯🇵</span>
<div>
<p class="text-sm uppercase tracking-wide text-slate-500">日本語</p>
<h3 class="font-heading text-xl">JLPT N5N1</h3>
</div>
</div>
<p class="mt-3 text-sm text-slate-600">Кандзи, чтения, примеры предложений и контекстные подсказки для каждого уровня JLPT.</p>
<div class="mt-4 flex flex-wrap gap-2 text-xs text-slate-700">
<span class="rounded-full bg-red-50 px-3 py-1 text-red-800">Кандзи + фуриганда</span>
<span class="rounded-full bg-blue-50 px-3 py-1 text-blue-800">Грамматика JLPT</span>
<span class="rounded-full bg-slate-100 px-3 py-1 text-slate-800">Сленг / 日常会話</span>
</div>
</div>
</div>
</div>
</div>
</section>
</template>

View File

@@ -0,0 +1,93 @@
<template>
<section class="py-14 sm:py-16 lg:py-20">
<div class="container mx-auto max-w-6xl px-4">
<div class="section-shell p-8 sm:p-10">
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div>
<p class="badge">Отслеживание прогресса</p>
<h2 class="font-heading text-2xl sm:text-3xl lg:text-4xl">Статистика и история повторений</h2>
<p class="mt-2 text-slate-600">Следи за ростом словаря, точностью ответов и прогресс-барами по уровням.</p>
</div>
<div class="text-sm text-slate-500">Данные всегда рядом в боте.</div>
</div>
<div class="mt-8 grid gap-6 md:grid-cols-2">
<div class="card-surface fade-up">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-slate-500">Статистика слов</p>
<h3 class="font-heading text-xl">2 450 слов в словаре</h3>
</div>
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-blue-50 text-blue-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4.5 19.5 9 15l4 4 6-6v6h-15Z" />
</svg>
</div>
</div>
<div class="mt-4 space-y-3">
<div>
<div class="flex items-center justify-between text-sm text-slate-600">
<span>История повторений</span>
<span>+18% за неделю</span>
</div>
<div class="mt-2 h-2 w-full rounded-full bg-slate-100">
<div class="h-2 w-4/5 rounded-full bg-blue-600"></div>
</div>
</div>
<div>
<div class="flex items-center justify-between text-sm text-slate-600">
<span>Точность заданий</span>
<span>92%</span>
</div>
<div class="mt-2 h-2 w-full rounded-full bg-slate-100">
<div class="h-2 w-11/12 rounded-full bg-emerald-500"></div>
</div>
</div>
</div>
</div>
<div class="card-surface fade-up" style="animation-delay: 0.12s">
<p class="text-sm text-slate-500">Прогресс по уровням</p>
<h3 class="font-heading text-lg">CEFR и JLPT в одном дашборде</h3>
<div class="mt-4 grid grid-cols-2 gap-3 text-sm text-slate-700">
<div class="rounded-xl bg-slate-100 p-3">
<div class="flex items-center justify-between">
<span>CEFR</span>
<span class="rounded-full bg-blue-50 px-2 py-1 text-xs text-blue-700">B2</span>
</div>
<div class="mt-2 h-2 w-full rounded-full bg-slate-200">
<div class="h-2 w-[72%] rounded-full bg-blue-600"></div>
</div>
</div>
<div class="rounded-xl bg-slate-100 p-3">
<div class="flex items-center justify-between">
<span>JLPT</span>
<span class="rounded-full bg-indigo-50 px-2 py-1 text-xs text-indigo-700">N3</span>
</div>
<div class="mt-2 h-2 w-full rounded-full bg-slate-200">
<div class="h-2 w-[64%] rounded-full bg-indigo-500"></div>
</div>
</div>
<div class="rounded-xl bg-slate-100 p-3">
<div class="flex items-center justify-between">
<span>Дни подряд</span>
<span class="rounded-full bg-green-50 px-2 py-1 text-xs text-green-700">12</span>
</div>
<div class="mt-2 h-2 w-full rounded-full bg-slate-200">
<div class="h-2 w-[90%] rounded-full bg-emerald-500"></div>
</div>
</div>
<div class="rounded-xl bg-slate-100 p-3">
<div class="flex items-center justify-between">
<span>Диалоги</span>
<span class="rounded-full bg-amber-50 px-2 py-1 text-xs text-amber-700">128</span>
</div>
<div class="mt-2 h-2 w-full rounded-full bg-slate-200">
<div class="h-2 w-[55%] rounded-full bg-cyan-500"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>

View File

@@ -0,0 +1,61 @@
<template>
<section class="py-14 sm:py-16 lg:py-20">
<div class="container mx-auto max-w-6xl px-4">
<div class="section-shell p-8 sm:p-10">
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div>
<p class="badge">Умный словарь</p>
<h2 class="font-heading text-2xl sm:text-3xl lg:text-4xl">Контекстный словарь с AI</h2>
<p class="mt-2 text-slate-600">Переводы, транскрипции, примеры и тематические подборки в одном окне бота.</p>
</div>
<div class="flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-4 py-2 text-sm text-slate-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6v12m6-6H6" />
</svg>
Добавляй слова вручную или импортом
</div>
</div>
<div class="mt-8 grid gap-6 lg:grid-cols-3">
<div class="card-surface fade-up lg:col-span-2">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-slate-500">Ручное добавление</p>
<h3 class="font-heading text-xl">AI анализирует и дополняет карточку</h3>
</div>
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-blue-50 text-blue-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 18 18 6m0 0H9m9 0v9" />
</svg>
</div>
</div>
<div class="mt-4 grid gap-3 sm:grid-cols-2">
<div class="rounded-xl bg-slate-100 p-4 text-sm text-slate-700">Перевод, транскрипция, примеры и синонимы.</div>
<div class="rounded-xl bg-slate-100 p-4 text-sm text-slate-700">Несколько значений с контекстами и пояснениями.</div>
<div class="rounded-xl bg-slate-100 p-4 text-sm text-slate-700">Тематические подборки по запросу, готовые к повторению.</div>
<div class="rounded-xl bg-slate-100 p-4 text-sm text-slate-700">Пагинация словаря для больших списков.</div>
</div>
</div>
<div class="card-surface fade-up" style="animation-delay: 0.12s">
<p class="text-sm text-slate-500">Импорт текста</p>
<h3 class="font-heading text-lg">Извлечение слов из книг и статей</h3>
<p class="mt-3 text-sm text-slate-600">Бот анализирует .txt и .md файлы, создаёт карточки с примерами использования.</p>
<div class="mt-4 space-y-2 text-sm text-slate-700">
<div class="flex items-center gap-2 rounded-lg bg-blue-50 px-3 py-2">
<span class="h-2 w-2 rounded-full bg-emerald-500"></span>
Импорт .txt / .md файлов
</div>
<div class="flex items-center gap-2 rounded-lg bg-indigo-50 px-3 py-2">
<span class="h-2 w-2 rounded-full bg-cyan-500"></span>
Автодобавление в нужные уровни CEFR и JLPT
</div>
<div class="flex items-center gap-2 rounded-lg bg-amber-50 px-3 py-2">
<span class="h-2 w-2 rounded-full bg-violet-500"></span>
Сохранение примеров для последующих заданий
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>

64
app/components/Tasks.vue Normal file
View File

@@ -0,0 +1,64 @@
<template>
<section class="py-14 sm:py-16 lg:py-20">
<div class="container mx-auto max-w-6xl px-4">
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div>
<p class="badge">Интерактивные задания</p>
<h2 class="font-heading text-2xl sm:text-3xl lg:text-4xl">Практика, которая адаптируется</h2>
<p class="mt-2 text-slate-600">AI оценивает ответы, принимает синонимы и подбирает следующую сложность.</p>
</div>
<div class="text-sm text-slate-500">Переключай режимы: перевод, пропуски, контекст.</div>
</div>
<div class="mt-8 grid gap-6 lg:grid-cols-3">
<div class="card-surface fade-up lg:col-span-2">
<div class="flex flex-wrap gap-2 text-xs text-slate-700">
<span class="rounded-full bg-blue-50 px-3 py-1 text-blue-800">Перевод слов в обе стороны</span>
<span class="rounded-full bg-indigo-50 px-3 py-1 text-indigo-800">Новые слова на основе уровня</span>
<span class="rounded-full bg-emerald-50 px-3 py-1 text-emerald-800">Заполнение пропусков</span>
<span class="rounded-full bg-amber-50 px-3 py-1 text-amber-800">Перевод по контексту</span>
</div>
<div class="mt-5 grid gap-3 sm:grid-cols-2">
<div class="rounded-2xl bg-slate-100 p-4 text-sm text-slate-700">
<div class="flex items-center justify-between">
<span class="font-semibold">Умная проверка</span>
<span class="rounded-full bg-emerald-100 px-3 py-1 text-xs text-emerald-700">Принимает синонимы</span>
</div>
<p class="mt-2 text-slate-600">AI сравнивает смысл ответа, а не только точное совпадение.</p>
</div>
<div class="rounded-2xl bg-slate-100 p-4 text-sm text-slate-700">
<div class="flex items-center justify-between">
<span class="font-semibold">Примеры после ответа</span>
<span class="rounded-full bg-cyan-100 px-3 py-1 text-xs text-cyan-700">Контекст</span>
</div>
<p class="mt-2 text-slate-600">После каждого ответа бот выдаёт примеры и пояснения, чтобы закрепить материал.</p>
</div>
</div>
</div>
<div class="card-surface fade-up" style="animation-delay: 0.12s">
<div class="flex items-center gap-3">
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-blue-50 text-blue-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12h6m-3-3v6m8-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<div>
<p class="text-sm text-slate-500">Адаптация под уровень</p>
<h3 class="font-heading text-xl">Сложность растёт вместе с тобой</h3>
</div>
</div>
<p class="mt-3 text-sm text-slate-600">Бот повышает сложность, когда ответы стабильны, и снижает, если нужна поддержка.</p>
<div class="mt-4 space-y-2 text-xs text-slate-700">
<div class="flex items-center justify-between rounded-lg bg-slate-100 px-3 py-2">
<span>CEFR A1C2</span>
<span class="rounded-full bg-blue-50 px-2 py-1 text-blue-700">EN</span>
</div>
<div class="flex items-center justify-between rounded-lg bg-slate-100 px-3 py-2">
<span>JLPT N5N1</span>
<span class="rounded-full bg-indigo-50 px-2 py-1 text-indigo-700">JP</span>
</div>
</div>
</div>
</div>
</div>
</section>
</template>

9
app/layouts/default.vue Normal file
View File

@@ -0,0 +1,9 @@
<template>
<div class="relative min-h-screen bg-slate-50 text-slate-900">
<div class="pointer-events-none absolute inset-0 gradient-ring"></div>
<div class="absolute inset-x-0 top-0 h-56 bg-gradient-to-br from-blue-100 via-indigo-50 to-transparent blur-3xl opacity-80"></div>
<main class="relative z-10">
<slot />
</main>
</div>
</template>

13
app/pages/index.vue Normal file
View File

@@ -0,0 +1,13 @@
<template>
<div class="relative">
<Hero />
<Features />
<Languages />
<SmartDictionary />
<Tasks />
<Dialogs />
<Progress />
<CTA />
<Footer />
</div>
</template>

70
assets/css/tailwind.css Normal file
View File

@@ -0,0 +1,70 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
color-scheme: light;
--font-heading: 'Space Grotesk', 'Manrope', system-ui, -apple-system, sans-serif;
--font-body: 'Manrope', system-ui, -apple-system, sans-serif;
}
body {
@apply bg-slate-50 text-slate-900 font-body antialiased;
}
@layer base {
.font-heading {
font-family: var(--font-heading);
}
.font-body {
font-family: var(--font-body);
}
}
@layer utilities {
.shadow-card {
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.12);
}
.shadow-floating {
box-shadow: 0 14px 42px rgba(59, 130, 246, 0.18);
}
}
.gradient-ring {
background: radial-gradient(circle at 20% 20%, rgba(59, 130, 246, 0.12), transparent 35%),
radial-gradient(circle at 80% 10%, rgba(14, 165, 233, 0.12), transparent 32%),
radial-gradient(circle at 50% 80%, rgba(99, 102, 241, 0.1), transparent 30%);
}
.section-shell {
@apply relative overflow-hidden rounded-3xl border border-slate-200 bg-white/95 backdrop-blur shadow-card;
}
.card-surface {
@apply rounded-2xl border border-slate-200 bg-white p-6 shadow-card transition duration-300;
}
.card-surface:hover {
transform: translateY(-0.25rem);
box-shadow: 0 12px 34px rgba(59, 130, 246, 0.16);
}
.badge {
@apply inline-flex items-center gap-2 rounded-full bg-blue-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-blue-700;
}
.fade-up {
opacity: 0;
animation: fade-up 0.9s ease forwards;
}
@keyframes fade-up {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

66
nuxt.config.ts Normal file
View File

@@ -0,0 +1,66 @@
import { defineNuxtConfig } from 'nuxt/config'
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
modules: [
'@nuxt/image-edge',
'@nuxt/fonts',
'@nuxtjs/tailwindcss',
'@nuxtjs/seo'
],
app: {
head: {
title: 'AI Telegram Bot — изучение английского и японского',
meta: [
{
name: 'description',
content:
'Умный Telegram-бот для изучения английского (A1C2) и японского (N5N1). Словарь, задания, диалоги, прогресс — всё в одном месте.'
},
{
property: 'og:title',
content: 'AI Telegram Bot — изучение английского и японского'
},
{
property: 'og:description',
content:
'Умный Telegram-бот для изучения английского (A1C2) и японского (N5N1). Словарь, задания, диалоги, прогресс — всё в одном месте.'
}
]
}
},
css: ['~/assets/css/tailwind.css'],
postcss: {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
},
// ⬇ fonts-блок можно вообще убрать, модуль @nuxt/fonts работает и без конфигурации
// если хочешь — потом вернём сюда тонкую настройку
// fonts: { ... }
// Это именно то, что ожидает Nuxt SEO: блок `site` в nuxt.config
site: {
url: process.env.NUXT_PUBLIC_SITE_URL || 'https://language.animeenigma.ru/',
name: 'Anime Enigma Language Bot',
description:
'Умный Telegram-бот для изучения английского (A1C2) и японского (N5N1). Словарь, задания, диалоги, прогресс — всё в одном месте.'
},
runtimeConfig: {
public: {
siteName: 'Anime Enigma Language Bot',
apiBase: process.env.NUXT_PUBLIC_API_BASE || 'http://localhost:8000'
}
},
tailwindcss: {
viewer: false
}
})

16266
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
package.json Normal file
View File

@@ -0,0 +1,26 @@
{
"name": "landing-eng-bot",
"type": "module",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@nuxtjs/seo": "^3.2.2",
"nuxt": "^4.2.1",
"vue": "^3.5.25",
"vue-router": "^4.6.3"
},
"devDependencies": {
"@nuxt/fonts": "^0.10.3",
"@nuxt/image-edge": "^1.3.0-28493895.369b747",
"@nuxtjs/tailwindcss": "^6.12.0",
"autoprefixer": "^10.4.22",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.18"
}
}

4
public/_robots.txt Normal file
View File

@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://language.animeenigma.ru/sitemap.xml

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

43
tailwind.config.ts Normal file
View File

@@ -0,0 +1,43 @@
import type { Config } from 'tailwindcss'
export default {
content: [
'./components/**/*.{vue,js,ts}',
'./layouts/**/*.{vue,js,ts}',
'./pages/**/*.{vue,js,ts}',
'./app/**/*.{vue,js,ts}',
'./nuxt.config.{js,ts}'
],
theme: {
extend: {
fontFamily: {
heading: ['var(--font-heading)', 'Space Grotesk', 'sans-serif'],
body: ['var(--font-body)', 'Manrope', 'sans-serif']
},
colors: {
primary: '#7C3AED',
secondary: '#2563EB',
accent: '#0EA5E9',
surface: '#0F172A',
card: '#0B1020'
},
backgroundImage: {
'hero-gradient': 'linear-gradient(135deg, #7C3AED 0%, #2563EB 50%, #0EA5E9 100%)',
'card-gradient': 'linear-gradient(145deg, rgba(124,58,237,0.12), rgba(37,99,235,0.08))'
},
boxShadow: {
glow: '0 10px 40px rgba(124, 58, 237, 0.35)'
},
keyframes: {
'fade-up': {
'0%': { opacity: '0', transform: 'translateY(20px)' },
'100%': { opacity: '1', transform: 'translateY(0)' }
}
},
animation: {
'fade-up': 'fade-up 0.9s ease forwards'
}
}
},
plugins: []
} satisfies Config

16
tsconfig.json Normal file
View File

@@ -0,0 +1,16 @@
{
"files": [],
"references": [
{ "path": "./.nuxt/tsconfig.app.json" },
{ "path": "./.nuxt/tsconfig.server.json" },
{ "path": "./.nuxt/tsconfig.shared.json" },
{ "path": "./.nuxt/tsconfig.node.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"~/*": ["./*"]
}
}
}