feat(trainer): add GitHub activity heatmap, 4-option quiz mode, active typing, and duplicate checks
This commit is contained in:
1 parent
d31c77fdd0
commit
2294121777
16 files changed
+4318
-685
No files matched your search
+673
-83
@@ -1,65 +1,93 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<html lang="ru" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Notes — Персональный тренажёр английского языка</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>Notes — Персональный тренажер и словарь английского языка</title>
|
||||
<meta name="description" content="Защищённый персональный тренажёр и словарь английского языка с интервальными повторениями SRS SM-2, контекстным поиском и озвучкой">
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📖</text></svg>">
|
||||
|
||||
<!-- PWA meta and icons -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta name="theme-color" content="#0f172a">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Notes English">
|
||||
<link rel="apple-touch-icon" href="/static/icons/icon-192.png">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Login Modal / Screen -->
|
||||
<div id="auth-screen" class="auth-screen hidden">
|
||||
<div class="auth-card">
|
||||
<div class="auth-header">
|
||||
<div class="app-logo">📖</div>
|
||||
<h2>Вход в Notes</h2>
|
||||
<p>Персональный словарь и тренажёр языка</p>
|
||||
<!-- Login Overlay / Page -->
|
||||
<div id="login-overlay" class="login-overlay hidden">
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<div class="login-logo">📖</div>
|
||||
<h1>Notes</h1>
|
||||
<p>Персональный словарь и тренажёр английского языка</p>
|
||||
</div>
|
||||
<form id="login-form">
|
||||
|
||||
<form id="login-form" class="login-form">
|
||||
<div class="form-group">
|
||||
<label for="username">Имя пользователя</label>
|
||||
<input type="text" id="username" autocomplete="username" required placeholder="admin" autofocus>
|
||||
<label for="login-username">Имя пользователя</label>
|
||||
<input type="text" id="login-username" class="input-styled" autocomplete="username" required autofocus placeholder="admin">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Пароль</label>
|
||||
<input type="password" id="password" autocomplete="current-password" required placeholder="••••••••">
|
||||
<label for="login-password">Пароль</label>
|
||||
<input type="password" id="login-password" class="input-styled" autocomplete="current-password" required placeholder="••••••••">
|
||||
</div>
|
||||
<div id="auth-error" class="error-banner hidden"></div>
|
||||
<button type="submit" id="login-btn" class="btn btn-primary btn-block">Войти в систему</button>
|
||||
|
||||
<div id="login-error" class="login-error hidden"></div>
|
||||
|
||||
<button type="submit" id="login-submit-btn" class="btn btn-primary btn-block">
|
||||
Войти в словарь
|
||||
</button>
|
||||
</form>
|
||||
<div class="auth-footer">
|
||||
<span>Защищённый контур: notes.qpid.pro</span>
|
||||
<div class="login-footer">
|
||||
<small>Безопасное хранилище словаря • notes.qpid.pro</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Application UI -->
|
||||
<div id="app-container" class="app-container hidden">
|
||||
<!-- Top Navigation -->
|
||||
<!-- Main Application Shell -->
|
||||
<div id="app-shell" class="app-shell">
|
||||
<!-- Top Navigation Bar -->
|
||||
<header class="navbar">
|
||||
<div class="nav-brand">
|
||||
<div class="nav-brand" id="nav-brand-home" title="На главную">
|
||||
<span class="brand-icon">📖</span>
|
||||
<span class="brand-title">Notes</span>
|
||||
<span class="brand-sub">English Lexicon</span>
|
||||
</div>
|
||||
|
||||
<div class="nav-stats" id="nav-stats">
|
||||
<span class="stat-badge" title="Всего слов в словаре">Всего: <strong id="stat-total">0</strong></span>
|
||||
<span class="stat-badge stat-today" title="Добавлено сегодня">Сегодня: <strong id="stat-today">+0</strong></span>
|
||||
<div class="nav-center">
|
||||
<div class="nav-stats" id="nav-stats">
|
||||
<span class="stat-badge" title="Всего слов в словаре">Всего: <strong id="stat-total">0</strong></span>
|
||||
<span class="stat-badge stat-today" title="Добавлено сегодня">Сегодня: <strong id="stat-today">+0</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav-actions">
|
||||
<button id="srs-trainer-btn" class="btn btn-trainer" title="Интервальный тренажер (Flashcards, 4 варианта, диктант, ввод)">
|
||||
<span class="trainer-icon">🎓</span>
|
||||
<span class="trainer-text">Тренажёр SRS</span>
|
||||
<span id="srs-badge" class="srs-due-badge">0</span>
|
||||
</button>
|
||||
<button id="stats-open-btn" class="btn btn-outline btn-sm stat-open-btn" title="Активность (GitHub Heatmap) и аналитика">
|
||||
<span>📊 Статистика</span>
|
||||
</button>
|
||||
<button id="export-open-btn" class="btn btn-outline btn-sm" title="Экспорт словаря">
|
||||
<span>📤 Экспорт</span>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-btn" title="Переключить тему">🌓</button>
|
||||
<div class="user-profile">
|
||||
<span id="user-display-name" class="user-badge">user</span>
|
||||
<button id="change-pwd-open-btn" class="btn btn-sm btn-outline" title="Сменить пароль">🔑 Пароль</button>
|
||||
<button id="logout-btn" class="btn btn-sm btn-outline" title="Выйти из аккаунта">Выход</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<!-- VIEW 1: Main Dictionary & Capture View -->
|
||||
<main class="main-content" id="dictionary-view">
|
||||
<!-- Search & Translation Section -->
|
||||
<section class="search-section">
|
||||
<div class="search-box-wrapper">
|
||||
@@ -70,6 +98,15 @@
|
||||
<button id="translate-btn" class="btn btn-primary translate-action-btn">Перевести</button>
|
||||
</div>
|
||||
|
||||
<!-- Duplicate Warning Notification -->
|
||||
<div id="duplicate-warning-box" class="duplicate-warning-box hidden">
|
||||
<div class="dup-left">
|
||||
<span class="dup-icon">⚠️</span>
|
||||
<span class="dup-text">Это слово уже есть в вашем словаре: <strong id="dup-term-label"></strong> (<em id="dup-trans-label"></em>)</span>
|
||||
</div>
|
||||
<button id="dup-open-btn" class="btn btn-xs btn-outline">Посмотреть / Дополнить</button>
|
||||
</div>
|
||||
|
||||
<div class="hotkey-hints">
|
||||
<span class="hint-item"><kbd>Enter</kbd> перевод</span>
|
||||
<span class="hint-item"><kbd>Ctrl</kbd>+<kbd>Enter</kbd> сохранить</span>
|
||||
@@ -78,58 +115,51 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loader -->
|
||||
<div id="lookup-spinner" class="lookup-spinner hidden">
|
||||
<div class="spinner"></div>
|
||||
<span>Ищем толкование, произношение и перевод...</span>
|
||||
</div>
|
||||
<!-- Live Translation & Dictionary Lookup Result Card -->
|
||||
<div id="lookup-card" class="lookup-card hidden">
|
||||
<div class="lookup-header">
|
||||
<div class="lookup-term-group">
|
||||
<h2 id="res-term" class="res-term">Word</h2>
|
||||
<span id="res-phonetic" class="res-phonetic">/wɜːd/</span>
|
||||
<button id="audio-play-btn" class="icon-btn audio-btn hidden" title="Прослушать произношение">🔊</button>
|
||||
</div>
|
||||
<span id="res-pos" class="badge-pos">Noun</span>
|
||||
</div>
|
||||
|
||||
<!-- Live Result Card -->
|
||||
<div id="result-card" class="result-card hidden">
|
||||
<div class="result-header">
|
||||
<div class="term-title-row">
|
||||
<h2 id="res-term" class="res-term">reluctant</h2>
|
||||
<span id="res-phonetic" class="res-phonetic badge-pill">/rɪˈlʌktənt/</span>
|
||||
<span id="res-pos" class="res-pos badge-pill">Adjective</span>
|
||||
<button id="play-audio-btn" class="audio-btn" title="Прослушать произношение">
|
||||
🔊 <span class="audio-text">Слушать</span>
|
||||
</button>
|
||||
<!-- English Definition if available -->
|
||||
<div id="res-definition-wrap" class="definition-wrap hidden">
|
||||
<div class="def-label">Толкование (EN):</div>
|
||||
<div id="res-definition" class="res-definition"></div>
|
||||
</div>
|
||||
|
||||
<!-- Primary Translation Field (Editable before saving) -->
|
||||
<div class="form-group edit-translation-group">
|
||||
<label for="edit-translation">Перевод на русский язык:</label>
|
||||
<div class="input-with-button">
|
||||
<input type="text" id="edit-translation" class="input-styled" placeholder="Русский перевод...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="res-definition-box" class="definition-box">
|
||||
<div class="def-label">Толкование (English):</div>
|
||||
<p id="res-definition" class="def-text"></p>
|
||||
<!-- Context & Extra Details Box -->
|
||||
<div class="context-group">
|
||||
<label for="edit-context">Контекстное предложение (где встретилось выражение):</label>
|
||||
<textarea id="edit-context" class="input-styled textarea-context" rows="2" placeholder="He was reluctant to admit his mistake during the team retrospective..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="card-edit-grid">
|
||||
<div class="field-group">
|
||||
<label for="edit-translation">Перевод на русский <span class="req">*</span></label>
|
||||
<input type="text" id="edit-translation" class="input-styled" placeholder="Основной перевод...">
|
||||
<div id="translation-suggestions" class="suggestions-chips"></div>
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label for="edit-context">Контекстное предложение (где встретилось?)</label>
|
||||
<input type="text" id="edit-context" class="input-styled" placeholder="Например: The switch drops the frame if the VLAN ID is invalid.">
|
||||
</div>
|
||||
|
||||
<!-- Organization: Collection & Tags Row -->
|
||||
<div class="meta-inputs-grid">
|
||||
<div class="field-row">
|
||||
<div class="field-group flex-2">
|
||||
<label for="edit-tags">Теги (через запятую)</label>
|
||||
<input type="text" id="edit-tags" class="input-styled" placeholder="general, phrasal-verb, networking">
|
||||
<div class="quick-tags" id="quick-tags-bar">
|
||||
<span class="tag-chip" data-tag="general">#general</span>
|
||||
<span class="tag-chip" data-tag="phrasal-verb">#phrasal-verb</span>
|
||||
<span class="tag-chip" data-tag="idiom">#idiom</span>
|
||||
<span class="tag-chip" data-tag="daily">#daily</span>
|
||||
<span class="tag-chip" data-tag="tech">#tech</span>
|
||||
<span class="tag-chip" data-tag="work">#work</span>
|
||||
</div>
|
||||
<div class="form-group flex-1">
|
||||
<label for="edit-collection">📁 Коллекция (папка):</label>
|
||||
<input type="text" id="edit-collection" list="collections-datalist" class="input-styled" value="General" placeholder="General, IT, Work...">
|
||||
<datalist id="collections-datalist"></datalist>
|
||||
</div>
|
||||
|
||||
<div class="field-group flex-1">
|
||||
<label for="edit-notes">Заметка / Мнемоника</label>
|
||||
<div class="form-group flex-1">
|
||||
<label for="edit-tags">🏷️ Теги (через запятую):</label>
|
||||
<input type="text" id="edit-tags" class="input-styled" placeholder="#phrasal-verb, #work, #daily" value="general">
|
||||
</div>
|
||||
<div class="form-group flex-1">
|
||||
<label for="edit-notes">💡 Заметка / Ассоциация:</label>
|
||||
<input type="text" id="edit-notes" class="input-styled" placeholder="Ассоциация...">
|
||||
</div>
|
||||
</div>
|
||||
@@ -152,15 +182,32 @@
|
||||
</div>
|
||||
|
||||
<div class="list-controls">
|
||||
<button id="batch-toggle-btn" class="btn btn-sm btn-outline batch-mode-btn" title="Режим множественного выбора">
|
||||
☑️ Выбрать
|
||||
</button>
|
||||
<div class="search-mini-box">
|
||||
<input type="text" id="filter-input" placeholder="Фильтр по слову, переводу, контексту...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Collections Filter Bar -->
|
||||
<div class="collections-filter-wrap">
|
||||
<div class="filter-header-bar">
|
||||
<span class="filter-title">📁 Коллекции:</span>
|
||||
<button id="new-collection-btn" class="btn btn-xs btn-ghost">+ Новая папка</button>
|
||||
</div>
|
||||
<div id="collections-filter-bar" class="filter-chips-row">
|
||||
<button class="col-chip active" data-col="">Все коллекции</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Active Tags Filter -->
|
||||
<div id="tags-filter-bar" class="tags-filter-bar">
|
||||
<button class="filter-chip active" data-tag="">Все теги</button>
|
||||
<div class="tags-filter-wrap">
|
||||
<span class="filter-title">🏷️ Теги:</span>
|
||||
<div id="tags-filter-bar" class="filter-chips-row">
|
||||
<button class="filter-chip active" data-tag="">Все теги</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cards Grid/List -->
|
||||
@@ -173,32 +220,446 @@
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- VIEW 2: SRS Trainer Interactive View -->
|
||||
<section id="srs-view" class="srs-view hidden">
|
||||
<!-- SRS Top Bar -->
|
||||
<header class="srs-topbar">
|
||||
<div class="srs-topbar-left">
|
||||
<button id="srs-exit-btn" class="btn btn-sm btn-outline">← В словарь</button>
|
||||
<div class="srs-mode-switch">
|
||||
<button class="srs-tab active" data-mode="flashcards">📇 Карточки</button>
|
||||
<button class="srs-tab" data-mode="quiz">🎯 4 Варианта</button>
|
||||
<button class="srs-tab" data-mode="typing">✍️ Набор слова</button>
|
||||
<button class="srs-tab" data-mode="cloze">🧩 Пропуски</button>
|
||||
<button class="srs-tab" data-mode="dictation">🎧 Диктант</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="srs-topbar-center">
|
||||
<div class="srs-progress-container">
|
||||
<div id="srs-progress-fill" class="srs-progress-fill" style="width: 0%;"></div>
|
||||
</div>
|
||||
<span id="srs-counter" class="srs-counter">Карточка 0 из 0</span>
|
||||
</div>
|
||||
|
||||
<div class="srs-topbar-right">
|
||||
<button id="srs-mix-btn" class="btn btn-sm btn-ghost" title="Умный микс дня: 10 новых + 10 повторов + 5 трудных слов">
|
||||
<span id="srs-mix-label">🎲 Микс дня: ВКЛ</span>
|
||||
</button>
|
||||
<button id="srs-dir-btn" class="btn btn-sm btn-secondary" title="Сменить направление">
|
||||
<span id="srs-dir-label">EN ➔ RU</span> ⇄
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- SRS Main Interactive Arena -->
|
||||
<div class="srs-arena">
|
||||
<!-- Loading State -->
|
||||
<div id="srs-loading" class="srs-state-box hidden">
|
||||
<div class="spinner"></div>
|
||||
<p>Загрузка карточек для тренировки...</p>
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div id="srs-empty" class="srs-state-box hidden">
|
||||
<div class="srs-big-icon">🎉</div>
|
||||
<h3>На сегодня всё повторено!</h3>
|
||||
<p>В очереди нет слов, требующих интервального повторения.</p>
|
||||
<div class="srs-empty-buttons">
|
||||
<button id="srs-train-all-btn" class="btn btn-primary btn-lg">Тренировать все слова вне очереди</button>
|
||||
<button id="srs-empty-back-btn" class="btn btn-outline btn-lg">Вернуться в словарь</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODE 1: Flashcards -->
|
||||
<div id="srs-mode-flashcards" class="srs-mode-container hidden">
|
||||
<div class="flashcard-wrapper" id="flashcard-flip-wrap">
|
||||
<div class="flashcard-inner" id="flashcard-inner">
|
||||
<!-- Card Front -->
|
||||
<div class="flashcard-face flashcard-front" id="flashcard-front">
|
||||
<div class="fc-badge-row">
|
||||
<span class="fc-dir-badge" id="fc-front-dir-badge">EN ➔ RU</span>
|
||||
<span class="fc-pos-badge" id="fc-front-pos">Adjective</span>
|
||||
</div>
|
||||
<div class="fc-center">
|
||||
<h1 class="fc-main-term" id="fc-front-main">reluctant</h1>
|
||||
<div class="fc-phonetic-row" id="fc-front-phonetic-wrap">
|
||||
<span class="res-phonetic" id="fc-front-phonetic">/rɪˈlʌktənt/</span>
|
||||
<button id="fc-front-audio-btn" class="icon-btn audio-round-btn" title="Озвучить (Ctrl+P / 🔊)">🔊</button>
|
||||
</div>
|
||||
<div class="fc-hint-wrapper">
|
||||
<button id="fc-hint-btn" class="btn btn-xs btn-ghost">💡 Показать контекст</button>
|
||||
<div id="fc-hint-box" class="fc-hint-content hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fc-footer-hint">
|
||||
<kbd>Space</kbd> или клик по карточке — перевернуть
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card Back -->
|
||||
<div class="flashcard-face flashcard-back" id="flashcard-back">
|
||||
<div class="fc-badge-row">
|
||||
<span class="fc-dir-badge">ОТВЕТ</span>
|
||||
<span class="fc-pos-badge" id="fc-back-pos">Adjective</span>
|
||||
</div>
|
||||
<div class="fc-back-content">
|
||||
<div class="fc-back-top">
|
||||
<h2 class="fc-back-term" id="fc-back-term">reluctant</h2>
|
||||
<button id="fc-back-audio-btn" class="icon-btn audio-round-btn" title="Озвучить">🔊</button>
|
||||
</div>
|
||||
<div class="fc-back-translation" id="fc-back-translation">неохотный, нежелающий</div>
|
||||
<div class="fc-back-detail-box" id="fc-back-def-box">
|
||||
<span class="detail-label">Толкование:</span>
|
||||
<p id="fc-back-def" class="detail-text"></p>
|
||||
</div>
|
||||
<div class="fc-back-detail-box" id="fc-back-context-box">
|
||||
<span class="detail-label">Контекст:</span>
|
||||
<p id="fc-back-context" class="detail-text"></p>
|
||||
</div>
|
||||
<div class="fc-back-detail-box" id="fc-back-notes-box">
|
||||
<span class="detail-label">Заметка:</span>
|
||||
<p id="fc-back-notes" class="detail-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flip button (mobile accessible) -->
|
||||
<div id="fc-flip-btn-wrap" class="fc-flip-btn-wrap">
|
||||
<button id="fc-flip-btn" class="btn btn-primary btn-block btn-lg">Показать ответ (Space)</button>
|
||||
</div>
|
||||
|
||||
<!-- Rating Buttons -->
|
||||
<div id="fc-rating-bar" class="srs-rating-bar hidden">
|
||||
<button class="btn-rate btn-rate-1" data-rating="1" title="Горячая клавиша: 1">
|
||||
<span class="rate-key">[1]</span>
|
||||
<span class="rate-name">Снова</span>
|
||||
<span class="rate-time">< 1 мин</span>
|
||||
</button>
|
||||
<button class="btn-rate btn-rate-2" data-rating="2" title="Горячая клавиша: 2">
|
||||
<span class="rate-key">[2]</span>
|
||||
<span class="rate-name">Трудно</span>
|
||||
<span class="rate-time">1 день</span>
|
||||
</button>
|
||||
<button class="btn-rate btn-rate-3" data-rating="3" title="Горячая клавиша: 3">
|
||||
<span class="rate-key">[3]</span>
|
||||
<span class="rate-name">Хорошо</span>
|
||||
<span class="rate-time">3 дня</span>
|
||||
</button>
|
||||
<button class="btn-rate btn-rate-4" data-rating="4" title="Горячая клавиша: 4">
|
||||
<span class="rate-key">[4]</span>
|
||||
<span class="rate-name">Легко</span>
|
||||
<span class="rate-time">7 дней</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODE 2: Multiple Choice Quiz (4 options) -->
|
||||
<div id="srs-mode-quiz" class="srs-mode-container hidden">
|
||||
<div class="trainer-card quiz-card">
|
||||
<div class="trainer-header">
|
||||
<span class="mode-tag">🎯 Выбор из 4 вариантов</span>
|
||||
<span class="trainer-pos" id="quiz-pos"></span>
|
||||
</div>
|
||||
|
||||
<div class="quiz-hero">
|
||||
<div class="quiz-prompt-row">
|
||||
<h2 id="quiz-term-display" class="quiz-term-display">word</h2>
|
||||
<button id="quiz-audio-btn" class="icon-btn audio-round-btn" title="Озвучить">🔊</button>
|
||||
</div>
|
||||
<div id="quiz-phonetic-display" class="res-phonetic"></div>
|
||||
<div id="quiz-context-hint" class="quiz-context-hint hidden"></div>
|
||||
</div>
|
||||
|
||||
<div class="quiz-options-grid" id="quiz-options-grid">
|
||||
<!-- 4 option buttons generated dynamically -->
|
||||
</div>
|
||||
|
||||
<div id="quiz-feedback-box" class="trainer-feedback-box hidden"></div>
|
||||
<div id="quiz-details-box" class="trainer-revealed-details hidden"></div>
|
||||
|
||||
<div id="quiz-next-bar" class="quiz-next-bar hidden">
|
||||
<button id="quiz-next-btn" class="btn btn-primary btn-lg btn-block">Следующее слово (Enter / Space)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODE 3: Active Typing Mode (Ru -> En) -->
|
||||
<div id="srs-mode-typing" class="srs-mode-container hidden">
|
||||
<div class="trainer-card">
|
||||
<div class="trainer-header">
|
||||
<span class="mode-tag">✍️ Набор слова на английском</span>
|
||||
<span class="trainer-pos" id="typing-pos"></span>
|
||||
</div>
|
||||
|
||||
<div class="typing-hero">
|
||||
<span class="hint-label">Переведите на английский язык:</span>
|
||||
<h2 id="typing-prompt-ru" class="typing-prompt-ru">слово</h2>
|
||||
<div id="typing-context-hint" class="typing-context-hint hidden"></div>
|
||||
</div>
|
||||
|
||||
<form id="typing-form" class="trainer-form">
|
||||
<div class="input-action-row">
|
||||
<input type="text" id="typing-input" class="input-styled input-hero" placeholder="Напечатайте английское слово или связку..." autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus>
|
||||
<button type="submit" id="typing-submit-btn" class="btn btn-primary">Проверить</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="typing-feedback-box" class="trainer-feedback-box hidden"></div>
|
||||
<div id="typing-details-box" class="trainer-revealed-details hidden"></div>
|
||||
|
||||
<!-- Rating bar for typing -->
|
||||
<div id="typing-rating-bar" class="srs-rating-bar hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODE 4: Cloze Deletion -->
|
||||
<div id="srs-mode-cloze" class="srs-mode-container hidden">
|
||||
<div class="trainer-card">
|
||||
<div class="trainer-header">
|
||||
<span class="mode-tag">🧩 Пропуски в контексте</span>
|
||||
<span class="trainer-pos" id="cloze-pos"></span>
|
||||
</div>
|
||||
|
||||
<div class="cloze-sentence-display" id="cloze-sentence-display">
|
||||
The switch <span class="gap-placeholder">_____</span> the frame if invalid.
|
||||
</div>
|
||||
|
||||
<div class="cloze-translation-hint">
|
||||
<span class="hint-label">Перевод целевого слова:</span>
|
||||
<strong id="cloze-target-translation">неохотный</strong>
|
||||
</div>
|
||||
|
||||
<form id="cloze-form" class="trainer-form">
|
||||
<div class="input-action-row">
|
||||
<input type="text" id="cloze-input" class="input-styled input-hero" placeholder="Введите пропущенное слово..." autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus>
|
||||
<button type="submit" id="cloze-submit-btn" class="btn btn-primary">Проверить</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="cloze-feedback-box" class="trainer-feedback-box hidden"></div>
|
||||
<div id="cloze-details-box" class="trainer-revealed-details hidden"></div>
|
||||
|
||||
<!-- Rating bar for cloze -->
|
||||
<div id="cloze-rating-bar" class="srs-rating-bar hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODE 5: Audio Dictation -->
|
||||
<div id="srs-mode-dictation" class="srs-mode-container hidden">
|
||||
<div class="trainer-card">
|
||||
<div class="trainer-header">
|
||||
<span class="mode-tag">🎧 Аудио-диктант</span>
|
||||
<button id="dictation-replay-btn" class="btn btn-sm btn-ghost audio-replay-btn">
|
||||
🔊 Повторить звук <span class="kbd-sub"><kbd>Space</kbd></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dictation-hero">
|
||||
<button id="dictation-big-play-btn" class="dictation-pulse-btn" title="Слушать произношение">
|
||||
🔊
|
||||
</button>
|
||||
<p class="dictation-instructions">Слушайте произношение нативного диктора и напечатайте слово</p>
|
||||
</div>
|
||||
|
||||
<div class="dictation-hint-wrap">
|
||||
<button id="dictation-toggle-hint-btn" class="btn btn-xs btn-ghost">💡 Показать перевод-подсказку</button>
|
||||
<span id="dictation-hint-text" class="dictation-hint-text hidden"></span>
|
||||
</div>
|
||||
|
||||
<form id="dictation-form" class="trainer-form">
|
||||
<div class="input-action-row">
|
||||
<input type="text" id="dictation-input" class="input-styled input-hero" placeholder="Напечатайте услышанное слово на английском..." autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus>
|
||||
<button type="submit" id="dictation-submit-btn" class="btn btn-primary">Проверить</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="dictation-feedback-box" class="trainer-feedback-box hidden"></div>
|
||||
<div id="dictation-details-box" class="trainer-revealed-details hidden"></div>
|
||||
|
||||
<!-- Rating bar for dictation -->
|
||||
<div id="dictation-rating-bar" class="srs-rating-bar hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Session Complete Summary Screen -->
|
||||
<div id="srs-summary" class="srs-summary-card hidden">
|
||||
<div class="summary-trophy">🏆</div>
|
||||
<h2>Сессия завершена!</h2>
|
||||
<p class="summary-lead">Вы успешно закрепили знания и освежили долговременную память.</p>
|
||||
|
||||
<div class="summary-metrics">
|
||||
<div class="metric-card">
|
||||
<span class="metric-val" id="sum-count">0</span>
|
||||
<span class="metric-desc">Повторено слов</span>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span class="metric-val text-success" id="sum-retention">100%</span>
|
||||
<span class="metric-desc">Успешность (Хорошо/Легко)</span>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span class="metric-val text-primary" id="sum-streak">🔥 1</span>
|
||||
<span class="metric-desc">Серия дней</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-rates-bar" id="sum-rates-breakdown"></div>
|
||||
|
||||
<div class="summary-buttons">
|
||||
<button id="sum-repeat-btn" class="btn btn-primary btn-lg">🔄 Повторить ещё сессию</button>
|
||||
<button id="sum-return-btn" class="btn btn-outline btn-lg">📚 Вернуться в словарь</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Sticky Bulk Action Bar for Batch Selection Mode -->
|
||||
<div id="batch-action-bar" class="batch-action-bar hidden">
|
||||
<div class="batch-left">
|
||||
<label class="batch-check-label">
|
||||
<input type="checkbox" id="batch-select-all" class="batch-master-checkbox">
|
||||
<span id="batch-count-label">Выбрано: 0</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="batch-actions">
|
||||
<button id="batch-move-btn" class="btn btn-sm btn-outline">📁 В коллекцию</button>
|
||||
<button id="batch-tag-btn" class="btn btn-sm btn-outline">🏷️ Добавить тег</button>
|
||||
<button id="batch-delete-btn" class="btn btn-sm btn-danger">🗑️ Удалить</button>
|
||||
<button id="batch-cancel-btn" class="btn btn-sm btn-ghost">✕ Отмена</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Analytics & Activity Heatmap Modal -->
|
||||
<div id="stats-modal" class="modal-backdrop hidden">
|
||||
<div class="modal-window stats-modal-window">
|
||||
<div class="modal-header">
|
||||
<h3>📊 Аналитика обучения & Активность</h3>
|
||||
<button id="close-stats-modal-btn" class="icon-btn">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- Streak & Core Stats Hero -->
|
||||
<div class="stats-hero-grid">
|
||||
<div class="stat-kpi-card streak-kpi">
|
||||
<div class="kpi-icon">🔥</div>
|
||||
<div class="kpi-value" id="kpi-current-streak">0</div>
|
||||
<div class="kpi-label">Текущая серия дней</div>
|
||||
<div class="kpi-sub" id="kpi-longest-streak">Рекорд: 0 дней</div>
|
||||
</div>
|
||||
<div class="stat-kpi-card">
|
||||
<div class="kpi-icon">🎯</div>
|
||||
<div class="kpi-value text-success" id="kpi-retention-rate">100%</div>
|
||||
<div class="kpi-label">Точность запоминания</div>
|
||||
<div class="kpi-sub" id="kpi-total-reviews">Всего повторений: 0</div>
|
||||
</div>
|
||||
<div class="stat-kpi-card">
|
||||
<div class="kpi-icon">📚</div>
|
||||
<div class="kpi-value" id="kpi-total-words">0</div>
|
||||
<div class="kpi-label">Слов в словаре</div>
|
||||
<div class="kpi-sub" id="kpi-today-added">Сегодня добавлено: +0</div>
|
||||
</div>
|
||||
<div class="stat-kpi-card">
|
||||
<div class="kpi-icon">🎓</div>
|
||||
<div class="kpi-value text-accent" id="kpi-cefr-level">A1</div>
|
||||
<div class="kpi-label">Оценка уровня</div>
|
||||
<div class="kpi-sub" id="kpi-cefr-desc">Начальный словарный запас</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GitHub-Style Contribution Heatmap -->
|
||||
<div class="heatmap-section">
|
||||
<div class="heatmap-header">
|
||||
<h4>🟩 Календарь активности (GitHub-style Heatmap)</h4>
|
||||
<div class="heatmap-legend">
|
||||
<span>Меньше</span>
|
||||
<span class="legend-cell lvl-0" title="0 повторений"></span>
|
||||
<span class="legend-cell lvl-1" title="1 повторение"></span>
|
||||
<span class="legend-cell lvl-2" title="2-3 повторения"></span>
|
||||
<span class="legend-cell lvl-3" title="4+ повторений"></span>
|
||||
<span>Больше</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="heatmap-container-wrap">
|
||||
<div id="heatmap-grid" class="heatmap-grid">
|
||||
<!-- Heatmap cells generated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="heatmap-footer-note">
|
||||
<small>Цвет ячейки показывает количество пройденных тестов и интервальных повторений за день.</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mastery Distribution Progress -->
|
||||
<div class="mastery-section">
|
||||
<h4>📈 Стадии освоения слов (SM-2 SRS)</h4>
|
||||
<div class="segmented-progress-bar">
|
||||
<div id="prog-mastered" class="prog-segment seg-mastered" style="width: 0%;" title="Выучено (интервал > 21 дня)"></div>
|
||||
<div id="prog-review" class="prog-segment seg-review" style="width: 0%;" title="Повторение"></div>
|
||||
<div id="prog-learning" class="prog-segment seg-learning" style="width: 0%;" title="В процессе (1-3 повтора)"></div>
|
||||
<div id="prog-new" class="prog-segment seg-new" style="width: 100%;" title="Новые слова"></div>
|
||||
</div>
|
||||
<div class="mastery-legend-row">
|
||||
<div class="m-legend-item"><span class="dot dot-mastered"></span> Выучено: <strong id="cnt-mastered">0</strong></div>
|
||||
<div class="m-legend-item"><span class="dot dot-review"></span> Закрепление: <strong id="cnt-review">0</strong></div>
|
||||
<div class="m-legend-item"><span class="dot dot-learning"></span> В процессе: <strong id="cnt-learning">0</strong></div>
|
||||
<div class="m-legend-item"><span class="dot dot-new"></span> Новые: <strong id="cnt-new">0</strong></div>
|
||||
<div class="m-legend-item text-danger"><span class="dot dot-leech"></span> Трудные слова: <strong id="cnt-leeches">0</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="close-stats-btn" class="btn btn-primary">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Card Modal -->
|
||||
<div id="edit-modal" class="modal-backdrop hidden">
|
||||
<div class="modal-window">
|
||||
<div class="modal-header">
|
||||
<h3>Редактирование карточки</h3>
|
||||
<h3>✏️ Редактировать карточку</h3>
|
||||
<button id="close-modal-btn" class="icon-btn">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="modal-card-id">
|
||||
<div class="form-group">
|
||||
<label>Слово / Фраза</label>
|
||||
<input type="text" id="modal-term" class="input-styled">
|
||||
<label>Слово / Выражение (EN)</label>
|
||||
<input type="text" id="modal-term" class="input-styled" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Перевод на русский</label>
|
||||
<input type="text" id="modal-translation" class="input-styled">
|
||||
<label>Перевод (RU)</label>
|
||||
<input type="text" id="modal-translation" class="input-styled" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Контекстное предложение</label>
|
||||
<input type="text" id="modal-context" class="input-styled">
|
||||
<textarea id="modal-context" class="input-styled" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<div class="form-group flex-1">
|
||||
<label>Транскрипция (IPA)</label>
|
||||
<input type="text" id="modal-phonetic" class="input-styled">
|
||||
</div>
|
||||
<div class="form-group flex-1">
|
||||
<label>Часть речи</label>
|
||||
<input type="text" id="modal-pos" class="input-styled">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Теги</label>
|
||||
<input type="text" id="modal-tags" class="input-styled">
|
||||
<label>Английское толкование</label>
|
||||
<textarea id="modal-def" class="input-styled" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<div class="form-group flex-1">
|
||||
<label>Коллекция (Папка)</label>
|
||||
<input type="text" id="modal-collection" class="input-styled">
|
||||
</div>
|
||||
<div class="form-group flex-1">
|
||||
<label>Теги</label>
|
||||
<input type="text" id="modal-tags" class="input-styled">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Заметки / Мнемоника</label>
|
||||
@@ -212,6 +673,135 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Export Modal -->
|
||||
<div id="export-modal" class="modal-backdrop hidden">
|
||||
<div class="modal-window export-modal-window">
|
||||
<div class="modal-header">
|
||||
<h3>📤 Экспорт словаря</h3>
|
||||
<button id="close-export-modal-btn" class="icon-btn">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="modal-lead" id="export-modal-desc">Выберите удобный формат для выгрузки карточек:</p>
|
||||
<div class="export-grid">
|
||||
<div class="export-tile" data-format="csv">
|
||||
<div class="export-tile-icon">📊</div>
|
||||
<div class="export-tile-info">
|
||||
<h4>CSV (Excel / Sheets)</h4>
|
||||
<p>Таблица со всеми полями, переводами и примерами (UTF-8 BOM для русского языка).</p>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-primary">Скачать</button>
|
||||
</div>
|
||||
|
||||
<div class="export-tile" data-format="anki">
|
||||
<div class="export-tile-icon">🗃️</div>
|
||||
<div class="export-tile-info">
|
||||
<h4>Колода для Anki (.tsv)</h4>
|
||||
<p>Готовый файл для импорта в Anki с транскрипцией, озвучкой и контекстом.</p>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-primary">Скачать</button>
|
||||
</div>
|
||||
|
||||
<div class="export-tile" data-format="json">
|
||||
<div class="export-tile-icon">📋</div>
|
||||
<div class="export-tile-info">
|
||||
<h4>JSON (Полный архив)</h4>
|
||||
<p>Полная структурированная резервная копия данных словаря.</p>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-primary">Скачать</button>
|
||||
</div>
|
||||
|
||||
<div class="export-tile" data-format="md">
|
||||
<div class="export-tile-icon">📝</div>
|
||||
<div class="export-tile-info">
|
||||
<h4>Markdown (.md)</h4>
|
||||
<p>Красивый структурированный конспект для Obsidian, Notion или чтения.</p>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-primary">Скачать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span id="export-scope-label" class="text-dim">Объём экспорта: все карточки</span>
|
||||
<button id="export-cancel-btn" class="btn btn-outline">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Move Collection Modal -->
|
||||
<div id="move-col-modal" class="modal-backdrop hidden">
|
||||
<div class="modal-window modal-sm">
|
||||
<div class="modal-header">
|
||||
<h3>📁 Переместить в коллекцию</h3>
|
||||
<button id="close-move-modal-btn" class="icon-btn">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="modal-lead" id="move-modal-count-label">Выберите коллекцию для выбранных карточек:</p>
|
||||
<div class="form-group">
|
||||
<label for="move-col-input">Название коллекции</label>
|
||||
<input type="text" id="move-col-input" list="move-collections-datalist" class="input-styled" placeholder="Например: Разговорный">
|
||||
<datalist id="move-collections-datalist"></datalist>
|
||||
</div>
|
||||
<div class="quick-tags" id="move-col-quick-list"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="move-col-cancel-btn" class="btn btn-outline">Отмена</button>
|
||||
<button id="move-col-confirm-btn" class="btn btn-primary">Переместить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New Collection Modal -->
|
||||
<div id="new-col-modal" class="modal-backdrop hidden">
|
||||
<div class="modal-window modal-sm">
|
||||
<div class="modal-header">
|
||||
<h3>📁 Новая коллекция</h3>
|
||||
<button id="close-new-col-modal-btn" class="icon-btn">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="new-col-name-input">Название новой коллекции</label>
|
||||
<input type="text" id="new-col-name-input" class="input-styled" placeholder="Например: Сериалы / Netflix">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="new-col-cancel-btn" class="btn btn-outline">Отмена</button>
|
||||
<button id="new-col-save-btn" class="btn btn-primary">Создать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Change Password Modal -->
|
||||
<div id="change-password-modal" class="modal-backdrop hidden">
|
||||
<div class="modal-window modal-sm">
|
||||
<div class="modal-header">
|
||||
<h3>🔑 Смена пароля</h3>
|
||||
<button id="close-change-pwd-btn" class="icon-btn">✕</button>
|
||||
</div>
|
||||
<form id="change-password-form">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="current-password-input">Текущий пароль</label>
|
||||
<input type="password" id="current-password-input" class="input-styled" autocomplete="current-password" required placeholder="••••••••">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-password-input">Новый пароль</label>
|
||||
<input type="password" id="new-password-input" class="input-styled" autocomplete="new-password" required minlength="6" placeholder="Минимум 6 символов">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm-password-input">Подтвердите новый пароль</label>
|
||||
<input type="password" id="confirm-password-input" class="input-styled" autocomplete="new-password" required minlength="6" placeholder="Повторите новый пароль">
|
||||
</div>
|
||||
<div id="change-pwd-error" class="error-banner hidden"></div>
|
||||
<div id="change-pwd-success" class="success-banner hidden"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="change-pwd-cancel-btn" class="btn btn-outline">Отмена</button>
|
||||
<button type="submit" id="change-pwd-save-btn" class="btn btn-primary">Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Audio Element for native streaming -->
|
||||
<audio id="global-audio-player" preload="none"></audio>
|
||||
|
||||
|
||||
Reference in new issue
Block a user