@dzhechkov/keysarium 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -3
- package/bin/cli.js +0 -0
- package/package.json +5 -3
- package/templates/.claude/commands/analyst-manual.md +38 -0
- package/templates/.claude/commands/casarium.md +55 -4
- package/templates/.claude/commands/feature-adr.md +8 -0
- package/templates/.claude/rules/trust-tiers.md +5 -0
- package/templates/.claude/shards/feature-adr.shard.md +64 -10
- package/templates/.claude/shards/phase-ai-factory.shard.md +109 -0
- package/templates/.claude/skills/ai-factory-mapper/README.md +90 -0
- package/templates/.claude/skills/ai-factory-mapper/SKILL.md +309 -0
- package/templates/.claude/skills/ai-factory-mapper/assets/report-template.md +123 -0
- package/templates/.claude/skills/ai-factory-mapper/references/catalog.md +230 -0
- package/templates/.claude/skills/ai-factory-mapper/references/coverage-formula.md +219 -0
- package/templates/.claude/skills/ai-factory-mapper/references/decomposition-checklist.md +183 -0
- package/templates/.claude/skills/ai-factory-mapper/references/pattern-library.md +312 -0
- package/templates/.claude/skills/ai-factory-mapper/scripts/build_docx_report.js +423 -0
- package/templates/.claude/skills/analyst-manual-full/SKILL.md +286 -0
- package/templates/.claude/skills/edu-site-generator/SKILL.md +254 -0
- package/templates/.claude/skills/edu-site-generator/examples/sample-course-structure.md +116 -0
- package/templates/.claude/skills/edu-site-generator/modules/00-content-analysis.md +84 -0
- package/templates/.claude/skills/edu-site-generator/modules/01-course-structure.md +75 -0
- package/templates/.claude/skills/edu-site-generator/modules/02-data-generation.md +186 -0
- package/templates/.claude/skills/edu-site-generator/modules/03-scaffold.md +249 -0
- package/templates/.claude/skills/edu-site-generator/modules/04-components.md +130 -0
- package/templates/.claude/skills/edu-site-generator/modules/05-gamification.md +163 -0
- package/templates/.claude/skills/edu-site-generator/modules/06-deploy.md +88 -0
- package/templates/.claude/skills/edu-site-generator/modules/07-verification.md +97 -0
- package/templates/.claude/skills/edu-site-generator/references/component-templates.md +436 -0
- package/templates/.claude/skills/edu-site-generator/references/data-schemas.md +164 -0
- package/templates/.claude/skills/edu-site-generator/references/exercise-catalog.md +110 -0
- package/templates/.claude/skills/edu-site-generator/references/tech-stack.md +69 -0
- package/templates/.claude/skills/feature-adr/SKILL.md +245 -59
- package/templates/.claude/skills/feature-adr/modules/03-adr.md +43 -0
- package/templates/.claude/skills/feature-adr/modules/03.5-ideation-swarm.md +180 -0
- package/templates/.claude/skills/feature-adr/modules/06-implementation-plan.md +56 -7
- package/templates/.claude/skills/feature-adr/modules/08-qe.md +87 -23
- package/templates/.claude/skills/feature-adr/modules/09-fleet-qe.md +305 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/README.md +143 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/brutal-honesty-review.md +240 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/chaos-engineering-resilience.md +163 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/code-goal-planner.md +446 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/enterprise-integration-testing.md +111 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/mutation-testing.md +236 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/performance-testing.md +315 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/qcsd-ideation-swarm.md +126 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/qcsd-production-swarm.md +108 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-coverage-analysis.md +193 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-requirements-validation.md +254 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/regression-testing.md +233 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/risk-based-testing.md +212 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/security-testing.md +311 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/shift-left-testing.md +231 -0
- package/templates/.claude/skills/feature-adr/references/agentic-qe/tdd-london-chicago.md +249 -0
- package/templates/.claude/skills/transcript-site-generator/SKILL.md +287 -0
- package/templates/.claude/skills/transcript-site-generator/examples/sample-text-only-site.md +123 -0
- package/templates/.claude/skills/transcript-site-generator/examples/sample-transcript-site.md +134 -0
- package/templates/.claude/skills/transcript-site-generator/modules/00-input-analysis.md +132 -0
- package/templates/.claude/skills/transcript-site-generator/modules/01-content-parsing.md +117 -0
- package/templates/.claude/skills/transcript-site-generator/modules/02-site-generation.md +293 -0
- package/templates/.claude/skills/transcript-site-generator/modules/03-interactivity.md +406 -0
- package/templates/.claude/skills/transcript-site-generator/modules/04-deploy.md +130 -0
- package/templates/.claude/skills/transcript-site-generator/modules/05-verification.md +155 -0
- package/templates/.claude/skills/transcript-site-generator/references/component-catalog.md +86 -0
- package/templates/.claude/skills/transcript-site-generator/references/data-schemas.md +123 -0
- package/templates/.claude/skills/transcript-site-generator/references/tech-stack.md +132 -0
- package/templates/CLAUDE.md +9 -2
- package/templates/docs/edu-site-generator/01-deployment-guide.md +911 -0
- package/templates/docs/edu-site-generator/02-admin-guide.md +901 -0
- package/templates/docs/edu-site-generator/03-user-guide.md +1008 -0
- package/templates/docs/edu-site-generator/04-infrastructure-requirements.md +489 -0
- package/templates/docs/edu-site-generator/05-architecture.md +902 -0
- package/templates/docs/edu-site-generator/06-user-flows.md +759 -0
- package/templates/docs/transcript-site-generator/01-deployment-guide.md +1042 -0
- package/templates/docs/transcript-site-generator/02-admin-guide.md +918 -0
- package/templates/docs/transcript-site-generator/03-user-guide.md +925 -0
- package/templates/docs/transcript-site-generator/04-infrastructure-requirements.md +482 -0
- package/templates/docs/transcript-site-generator/05-architecture.md +1068 -0
- package/templates/docs/transcript-site-generator/06-user-flows.md +855 -0
|
@@ -0,0 +1,902 @@
|
|
|
1
|
+
# Архитектура @dzhechkov/skills-edu-site
|
|
2
|
+
|
|
3
|
+
## Содержание
|
|
4
|
+
|
|
5
|
+
1. [Обзор системы и философия проектирования](#обзор-системы-и-философия-проектирования)
|
|
6
|
+
2. [Компоненты системы](#компоненты-системы)
|
|
7
|
+
3. [MODULE: Content Analysis](#module-content-analysis)
|
|
8
|
+
4. [MODULE: Course Structure](#module-course-structure)
|
|
9
|
+
5. [MODULE: Data Generation](#module-data-generation)
|
|
10
|
+
6. [MODULE: Scaffold](#module-scaffold)
|
|
11
|
+
7. [MODULE: Components](#module-components)
|
|
12
|
+
8. [MODULE: Gamification](#module-gamification)
|
|
13
|
+
9. [MODULE: Deploy](#module-deploy)
|
|
14
|
+
10. [MODULE: Verification](#module-verification)
|
|
15
|
+
11. [Архитектура данных](#архитектура-данных)
|
|
16
|
+
12. [Иерархия компонентов](#иерархия-компонентов)
|
|
17
|
+
13. [State management: Zustand](#state-management-zustand)
|
|
18
|
+
14. [Exercise Engine](#exercise-engine)
|
|
19
|
+
15. [Обоснование технологического стека](#обоснование-технологического-стека)
|
|
20
|
+
16. [SEO-архитектура](#seo-архитектура)
|
|
21
|
+
17. [Расширяемость](#расширяемость)
|
|
22
|
+
18. [Проектные решения и компромиссы](#проектные-решения-и-компромиссы)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Обзор системы и философия проектирования
|
|
27
|
+
|
|
28
|
+
`@dzhechkov/skills-edu-site` — это скилл для Claude Code, реализующий полный цикл генерации образовательных SPA-приложений. Система принимает на вход документацию, URL, текст или описание темы и производит полностью рабочее React-приложение с интерактивными упражнениями, геймификацией и готовностью к деплою.
|
|
29
|
+
|
|
30
|
+
### Фундаментальная идея
|
|
31
|
+
|
|
32
|
+
Образовательный контент имеет предсказуемую структуру: тема разбивается на секции, секции содержат факты для изучения, факты можно проверить через упражнения. Edu-site автоматизирует этот процесс:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
ВХОД ОБРАБОТКА ВЫХОД
|
|
36
|
+
---------- ----------- --------
|
|
37
|
+
Документация -> Content Analysis -> React SPA
|
|
38
|
+
URL -> Course Structure -> с 6 типами
|
|
39
|
+
Текст -> Data Generation -> упражнений,
|
|
40
|
+
Тема -> Scaffold + Components -> геймификацией,
|
|
41
|
+
Gamification + Deploy -> прогрессом,
|
|
42
|
+
Verification -> финальным тестом
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Ключевые принципы проектирования
|
|
46
|
+
|
|
47
|
+
**1. SPA-first (Single Page Application)**
|
|
48
|
+
Всё приложение — один HTML-файл + бандл JS/CSS. Нет серверной части, нет базы данных, нет API-вызовов в рантайме. Это радикально упрощает деплой и обслуживание.
|
|
49
|
+
|
|
50
|
+
**2. Gamification-first**
|
|
51
|
+
Геймификация — не надстройка, а основа UX. Каждое взаимодействие приносит очки. Достижения мотивируют завершить курс. Progress bar создаёт ощущение продвижения.
|
|
52
|
+
|
|
53
|
+
**3. Content-driven architecture**
|
|
54
|
+
Данные отделены от представления. Четыре файла данных (`sections.js`, `exercises.js`, `quizQuestions.js`, `achievements.js`) определяют весь контент курса. Компоненты — универсальные рендереры, которые работают с любым контентом.
|
|
55
|
+
|
|
56
|
+
**4. Zero-config deployment**
|
|
57
|
+
Сгенерированный проект включает GitHub Actions workflow. Один `git push` — и сайт задеплоен. Никаких дополнительных настроек.
|
|
58
|
+
|
|
59
|
+
**5. Offline-capable by design**
|
|
60
|
+
Все данные встроены в бандл. Прогресс сохраняется в localStorage. Сайт работает полностью автономно после первой загрузки.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Компоненты системы
|
|
65
|
+
|
|
66
|
+
### Текстовая карта компонентов
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
@dzhechkov/skills-edu-site
|
|
70
|
+
+-- SKILL.md <- Точка входа, оркестратор 8 шагов
|
|
71
|
+
|
|
|
72
|
+
+-- modules/ <- 8 модулей пайплайна
|
|
73
|
+
| +-- 01-content-analysis.md <- Анализ контента и язык
|
|
74
|
+
| +-- 02-course-structure.md <- Структура курса и маппинг
|
|
75
|
+
| +-- 03-data-generation.md <- 4 файла данных
|
|
76
|
+
| +-- 04-scaffold.md <- Vite + React + Tailwind каркас
|
|
77
|
+
| +-- 05-components.md <- React-компоненты
|
|
78
|
+
| +-- 06-gamification.md <- Zustand store + achievements
|
|
79
|
+
| +-- 07-deploy.md <- GitHub Actions workflow
|
|
80
|
+
| +-- 08-verification.md <- 5 финальных проверок
|
|
81
|
+
|
|
|
82
|
+
+-- references/ <- Справочные материалы
|
|
83
|
+
| +-- exercise-types.md <- 6 типов упражнений
|
|
84
|
+
| +-- component-patterns.md <- React 19 паттерны
|
|
85
|
+
| +-- tailwind-v4-guide.md <- TailwindCSS v4 API
|
|
86
|
+
| +-- zustand-patterns.md <- Zustand persist + selectors
|
|
87
|
+
| +-- achievement-templates.md <- Шаблоны достижений
|
|
88
|
+
|
|
|
89
|
+
+-- examples/ <- Примеры данных
|
|
90
|
+
+-- sample-sections.js <- Пример sections.js
|
|
91
|
+
+-- sample-exercises.js <- Пример exercises.js (все 6 типов)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
.claude/commands/
|
|
96
|
+
+-- edu-site.md <- /edu-site -- полный пайплайн
|
|
97
|
+
|
|
98
|
+
.claude/rules/
|
|
99
|
+
+-- edu-site-conventions.md <- Правила генерации (автоматически)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Mermaid-диаграмма компонентов
|
|
103
|
+
|
|
104
|
+
```mermaid
|
|
105
|
+
graph TB
|
|
106
|
+
subgraph "Точка входа"
|
|
107
|
+
CMD["/edu-site<br/>command"]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
subgraph "Ядро Skill"
|
|
111
|
+
SKILL["SKILL.md<br/>Оркестратор"]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
subgraph "Модули пайплайна"
|
|
115
|
+
M1["01-content-analysis"]
|
|
116
|
+
M2["02-course-structure"]
|
|
117
|
+
M3["03-data-generation"]
|
|
118
|
+
M4["04-scaffold"]
|
|
119
|
+
M5["05-components"]
|
|
120
|
+
M6["06-gamification"]
|
|
121
|
+
M7["07-deploy"]
|
|
122
|
+
M8["08-verification"]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
subgraph "Справочники"
|
|
126
|
+
REF_EX["exercise-types.md"]
|
|
127
|
+
REF_COMP["component-patterns.md"]
|
|
128
|
+
REF_TW["tailwind-v4-guide.md"]
|
|
129
|
+
REF_ZU["zustand-patterns.md"]
|
|
130
|
+
REF_ACH["achievement-templates.md"]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
subgraph "Правила"
|
|
134
|
+
RULES["edu-site-conventions.md"]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
CMD --> SKILL
|
|
138
|
+
SKILL --> M1 --> M2 --> M3 --> M4 --> M5 --> M6 --> M7 --> M8
|
|
139
|
+
M2 --> REF_EX
|
|
140
|
+
M5 --> REF_COMP
|
|
141
|
+
M4 --> REF_TW
|
|
142
|
+
M6 --> REF_ZU
|
|
143
|
+
M6 --> REF_ACH
|
|
144
|
+
RULES -.->|"автоматически"| SKILL
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## MODULE: Content Analysis
|
|
150
|
+
|
|
151
|
+
### Назначение
|
|
152
|
+
|
|
153
|
+
Первый шаг пайплайна. Принимает произвольный вход (URL, файл, текст, описание темы) и производит структурированное представление контента.
|
|
154
|
+
|
|
155
|
+
### Архитектура входных данных
|
|
156
|
+
|
|
157
|
+
```mermaid
|
|
158
|
+
graph LR
|
|
159
|
+
INPUT["Вход пользователя"] --> DETECT{"Тип входа?"}
|
|
160
|
+
|
|
161
|
+
DETECT -->|"URL (http/https)"| FETCH["Fetch страницы<br/>Follow links (depth 3)"]
|
|
162
|
+
DETECT -->|"Путь к файлу"| READ["Read файл(ы)<br/>MD / TXT / HTML"]
|
|
163
|
+
DETECT -->|"Длинный текст"| PARSE["Parse text<br/>Извлечь структуру"]
|
|
164
|
+
DETECT -->|"Короткое описание"| GEN["Generative mode<br/>Создать контент"]
|
|
165
|
+
|
|
166
|
+
FETCH --> CLEAN["Очистка:<br/>убрать nav, footer,<br/>boilerplate"]
|
|
167
|
+
READ --> CLEAN
|
|
168
|
+
PARSE --> CLEAN
|
|
169
|
+
GEN --> TOPICS["Извлечение тем"]
|
|
170
|
+
|
|
171
|
+
CLEAN --> TOPICS
|
|
172
|
+
TOPICS --> LANG["Определение языка<br/>(ru/en)"]
|
|
173
|
+
LANG --> ANALYSIS["Structured Analysis:<br/>topics, concepts,<br/>terms, code examples"]
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Определение языка
|
|
177
|
+
|
|
178
|
+
Языковой детектор анализирует первые 1000 символов контента:
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
Стратегия:
|
|
182
|
+
1. Если > 60% символов кириллица -> ru
|
|
183
|
+
2. Если > 60% символов латиница -> en
|
|
184
|
+
3. Если смешанный -> по преобладанию
|
|
185
|
+
4. Если пользователь указал --lang -> принудительно
|
|
186
|
+
5. Fallback: en
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Выходные данные модуля
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
{
|
|
193
|
+
language: "en" | "ru",
|
|
194
|
+
topics: [{ name, relevance, subtopics }],
|
|
195
|
+
concepts: [{ term, definition, frequency }],
|
|
196
|
+
codeExamples: [{ code, language, context }],
|
|
197
|
+
estimatedSections: number,
|
|
198
|
+
contentLength: number,
|
|
199
|
+
sourceType: "url" | "file" | "text" | "topic"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## MODULE: Course Structure
|
|
206
|
+
|
|
207
|
+
### Назначение
|
|
208
|
+
|
|
209
|
+
Преобразует результат Content Analysis в иерархию секций курса с назначением типов упражнений.
|
|
210
|
+
|
|
211
|
+
### Алгоритм построения структуры
|
|
212
|
+
|
|
213
|
+
```mermaid
|
|
214
|
+
graph TD
|
|
215
|
+
TOPICS["Темы из Content Analysis"] --> CLUSTER["Кластеризация тем<br/>по смысловой близости"]
|
|
216
|
+
CLUSTER --> SECTIONS["Формирование секций<br/>(1 кластер = 1 секция)"]
|
|
217
|
+
SECTIONS --> ORDER["Определение порядка<br/>(от базовых к продвинутым)"]
|
|
218
|
+
ORDER --> EXERCISES["Назначение типов<br/>упражнений"]
|
|
219
|
+
EXERCISES --> VALIDATE["Валидация:<br/>min 2, max 6<br/>упражнений на секцию"]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Маппинг контента на типы упражнений
|
|
223
|
+
|
|
224
|
+
| Тип контента | Сигналы обнаружения | Назначаемый тип |
|
|
225
|
+
|-------------|-------------------|----------------|
|
|
226
|
+
| Определения, словарь | "is a", "means", "refers to" | Flashcards |
|
|
227
|
+
| CLI-команды | команды с флагами, `$`, `>` | CommandBuilder |
|
|
228
|
+
| Пошаговые процессы | "step 1", "then", "after" | DragToOrder |
|
|
229
|
+
| Парные концепции | "X vs Y", таблицы соответствий | Matching |
|
|
230
|
+
| Решения, best practices | "should", "avoid", "recommended" | ScenarioGame |
|
|
231
|
+
| Факты, теория | вопросительные конструкции | Quiz |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## MODULE: Data Generation
|
|
236
|
+
|
|
237
|
+
### Назначение
|
|
238
|
+
|
|
239
|
+
Генерация 4 JavaScript-файлов с данными курса.
|
|
240
|
+
|
|
241
|
+
### 4 файла данных
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
src/data/
|
|
245
|
+
+-- sections.js <- Секции курса с описаниями
|
|
246
|
+
+-- exercises.js <- Упражнения всех 6 типов
|
|
247
|
+
+-- quizQuestions.js <- Вопросы финального теста
|
|
248
|
+
+-- achievements.js <- Достижения с критериями
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Связи между файлами данных
|
|
252
|
+
|
|
253
|
+
```mermaid
|
|
254
|
+
graph LR
|
|
255
|
+
subgraph "Файлы данных"
|
|
256
|
+
SEC["sections.js<br/>id, title, description"]
|
|
257
|
+
EX["exercises.js<br/>sectionId, type, data"]
|
|
258
|
+
QUIZ["quizQuestions.js<br/>sectionId, question"]
|
|
259
|
+
ACH["achievements.js<br/>criteria: type, count"]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
SEC -->|"sectionId"| EX
|
|
263
|
+
SEC -->|"sectionId"| QUIZ
|
|
264
|
+
EX -->|"exercise_count"| ACH
|
|
265
|
+
SEC -->|"section_complete"| ACH
|
|
266
|
+
QUIZ -->|"final_test_pass"| ACH
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Ключевое правило:** Все `sectionId` в `exercises.js` и `quizQuestions.js` должны ссылаться на существующие `id` в `sections.js`. Это проверяется на шаге Verification.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## MODULE: Scaffold
|
|
274
|
+
|
|
275
|
+
### Назначение
|
|
276
|
+
|
|
277
|
+
Создание каркаса Vite + React + TailwindCSS v4 проекта.
|
|
278
|
+
|
|
279
|
+
### Генерируемая структура
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
project-root/
|
|
283
|
+
+-- package.json <- Зависимости и скрипты
|
|
284
|
+
+-- vite.config.js <- Vite конфигурация + base path
|
|
285
|
+
+-- index.html <- Entry HTML + SEO теги
|
|
286
|
+
+-- src/
|
|
287
|
+
| +-- main.jsx <- ReactDOM.createRoot
|
|
288
|
+
| +-- App.jsx <- HashRouter + Routes + Suspense
|
|
289
|
+
| +-- app.css <- TailwindCSS v4 @import + @theme
|
|
290
|
+
+-- .github/
|
|
291
|
+
+-- workflows/
|
|
292
|
+
+-- deploy.yml <- GitHub Actions workflow
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Конфигурация Vite
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
import { defineConfig } from 'vite'
|
|
299
|
+
import react from '@vitejs/plugin-react'
|
|
300
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
301
|
+
|
|
302
|
+
export default defineConfig({
|
|
303
|
+
base: '/<repo-name>/', // настраивается под GitHub Pages
|
|
304
|
+
plugins: [
|
|
305
|
+
react(),
|
|
306
|
+
tailwindcss(), // TailwindCSS v4 через Vite плагин
|
|
307
|
+
],
|
|
308
|
+
})
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Почему HashRouter
|
|
312
|
+
|
|
313
|
+
GitHub Pages не поддерживает серверный routing (нет fallback на `index.html` для произвольных путей). `HashRouter` использует URL-хэш (`#/section/1`) вместо path-based routing (`/section/1`), что корректно работает без серверной конфигурации.
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## MODULE: Components
|
|
318
|
+
|
|
319
|
+
### Назначение
|
|
320
|
+
|
|
321
|
+
Генерация всех React-компонентов приложения по 5 категориям.
|
|
322
|
+
|
|
323
|
+
### Карта компонентов
|
|
324
|
+
|
|
325
|
+
```
|
|
326
|
+
src/components/
|
|
327
|
+
+-- layout/
|
|
328
|
+
| +-- Header.jsx <- Логотип, навигация, очки, dark mode toggle
|
|
329
|
+
| +-- Footer.jsx <- Копирайт, ссылки
|
|
330
|
+
| +-- Sidebar.jsx <- Общий прогресс, список секций
|
|
331
|
+
| +-- Navigation.jsx <- Хлебные крошки, next/prev секция
|
|
332
|
+
|
|
|
333
|
+
+-- interactive/
|
|
334
|
+
| +-- Quiz.jsx <- Вопрос + варианты ответа
|
|
335
|
+
| +-- Flashcards.jsx <- Карточка: flip, known/review
|
|
336
|
+
| +-- Matching.jsx <- Drag-and-drop matching
|
|
337
|
+
| +-- DragToOrder.jsx <- Drag-and-drop ordering
|
|
338
|
+
| +-- CommandBuilder.jsx <- Сборка команды из частей
|
|
339
|
+
| +-- ScenarioGame.jsx <- Ситуация + выбор действия
|
|
340
|
+
|
|
|
341
|
+
+-- common/
|
|
342
|
+
| +-- Toast.jsx <- Уведомления (достижения, прогресс)
|
|
343
|
+
| +-- ProgressBar.jsx <- Полоса прогресса
|
|
344
|
+
| +-- Badge.jsx <- Бейдж достижения
|
|
345
|
+
| +-- AchievementPopup.jsx <- Попап разблокированного достижения
|
|
346
|
+
| +-- Button.jsx <- Стилизованная кнопка
|
|
347
|
+
| +-- Card.jsx <- Карточка-контейнер
|
|
348
|
+
|
|
|
349
|
+
+-- sections/
|
|
350
|
+
+-- SectionList.jsx <- Список секций на главной
|
|
351
|
+
+-- ExerciseRenderer.jsx <- Маршрутизатор: type -> компонент
|
|
352
|
+
|
|
353
|
+
src/pages/
|
|
354
|
+
+-- HomePage.jsx <- Главная: список секций + прогресс
|
|
355
|
+
+-- SectionPage.jsx <- Страница секции: контент + упражнения
|
|
356
|
+
+-- FinalTestPage.jsx <- Финальный тест: все вопросы
|
|
357
|
+
+-- ResultsPage.jsx <- Результаты теста: оценка + breakdown
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Иерархия рендеринга
|
|
361
|
+
|
|
362
|
+
```mermaid
|
|
363
|
+
graph TD
|
|
364
|
+
APP["App.jsx<br/>(HashRouter + Suspense)"]
|
|
365
|
+
|
|
366
|
+
APP --> HOME["HomePage<br/>(lazy loaded)"]
|
|
367
|
+
APP --> SECTION["SectionPage<br/>(lazy loaded)"]
|
|
368
|
+
APP --> FINAL["FinalTestPage<br/>(lazy loaded)"]
|
|
369
|
+
APP --> RESULTS["ResultsPage<br/>(lazy loaded)"]
|
|
370
|
+
|
|
371
|
+
HOME --> HEADER["Header"]
|
|
372
|
+
HOME --> SIDEBAR["Sidebar"]
|
|
373
|
+
HOME --> SLIST["SectionList"]
|
|
374
|
+
HOME --> FOOTER["Footer"]
|
|
375
|
+
|
|
376
|
+
SECTION --> HEADER2["Header"]
|
|
377
|
+
SECTION --> NAV["Navigation"]
|
|
378
|
+
SECTION --> CONTENT["Section Content"]
|
|
379
|
+
SECTION --> EXRENDER["ExerciseRenderer"]
|
|
380
|
+
SECTION --> FOOTER2["Footer"]
|
|
381
|
+
|
|
382
|
+
EXRENDER --> QUIZ["Quiz"]
|
|
383
|
+
EXRENDER --> FLASH["Flashcards"]
|
|
384
|
+
EXRENDER --> MATCH["Matching"]
|
|
385
|
+
EXRENDER --> DRAG["DragToOrder"]
|
|
386
|
+
EXRENDER --> CMD["CommandBuilder"]
|
|
387
|
+
EXRENDER --> SCEN["ScenarioGame"]
|
|
388
|
+
|
|
389
|
+
FINAL --> HEADER3["Header"]
|
|
390
|
+
FINAL --> QUESTIONS["QuizQuestions"]
|
|
391
|
+
|
|
392
|
+
RESULTS --> HEADER4["Header"]
|
|
393
|
+
RESULTS --> SCORE["ScoreBreakdown"]
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### ExerciseRenderer: маршрутизация по типу
|
|
397
|
+
|
|
398
|
+
```jsx
|
|
399
|
+
// src/components/sections/ExerciseRenderer.jsx
|
|
400
|
+
import { lazy, Suspense } from 'react'
|
|
401
|
+
|
|
402
|
+
const exerciseComponents = {
|
|
403
|
+
'quiz': lazy(() => import('../interactive/Quiz')),
|
|
404
|
+
'flashcards': lazy(() => import('../interactive/Flashcards')),
|
|
405
|
+
'matching': lazy(() => import('../interactive/Matching')),
|
|
406
|
+
'drag-to-order': lazy(() => import('../interactive/DragToOrder')),
|
|
407
|
+
'command-builder': lazy(() => import('../interactive/CommandBuilder')),
|
|
408
|
+
'scenario-game': lazy(() => import('../interactive/ScenarioGame')),
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export function ExerciseRenderer({ exercise, onComplete }) {
|
|
412
|
+
const Component = exerciseComponents[exercise.type]
|
|
413
|
+
if (!Component) return null
|
|
414
|
+
|
|
415
|
+
return (
|
|
416
|
+
<Suspense fallback={<div>Loading exercise...</div>}>
|
|
417
|
+
<Component
|
|
418
|
+
data={exercise.data}
|
|
419
|
+
onComplete={(points) => onComplete(exercise.id, points)}
|
|
420
|
+
/>
|
|
421
|
+
</Suspense>
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## MODULE: Gamification
|
|
429
|
+
|
|
430
|
+
### Назначение
|
|
431
|
+
|
|
432
|
+
Настройка Zustand store с persist middleware, системы очков, достижений, progress tracking и toast-уведомлений.
|
|
433
|
+
|
|
434
|
+
### Архитектура Zustand store
|
|
435
|
+
|
|
436
|
+
```mermaid
|
|
437
|
+
graph TD
|
|
438
|
+
subgraph "Zustand Store (useStore)"
|
|
439
|
+
subgraph "State"
|
|
440
|
+
PROGRESS["completedExercises<br/>sectionProgress"]
|
|
441
|
+
POINTS["totalPoints<br/>streak<br/>longestStreak"]
|
|
442
|
+
ACHIEVEMENTS["unlockedAchievements"]
|
|
443
|
+
FINAL["finalTestScore<br/>finalTestAnswers"]
|
|
444
|
+
UI["currentSection<br/>toastQueue"]
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
subgraph "Actions"
|
|
448
|
+
A1["completeExercise()"]
|
|
449
|
+
A2["unlockAchievement()"]
|
|
450
|
+
A3["submitFinalTest()"]
|
|
451
|
+
A4["showToast()"]
|
|
452
|
+
A5["resetProgress()"]
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
subgraph "Middleware"
|
|
456
|
+
PERSIST["persist<br/>(localStorage)"]
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
A1 --> PROGRESS
|
|
461
|
+
A1 --> POINTS
|
|
462
|
+
A1 -->|"check criteria"| A2
|
|
463
|
+
A2 --> ACHIEVEMENTS
|
|
464
|
+
A2 --> A4
|
|
465
|
+
A3 --> FINAL
|
|
466
|
+
A3 -->|"check criteria"| A2
|
|
467
|
+
A4 --> UI
|
|
468
|
+
|
|
469
|
+
PERSIST --> PROGRESS
|
|
470
|
+
PERSIST --> POINTS
|
|
471
|
+
PERSIST --> ACHIEVEMENTS
|
|
472
|
+
PERSIST --> FINAL
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Поток геймификации при выполнении упражнения
|
|
476
|
+
|
|
477
|
+
```mermaid
|
|
478
|
+
sequenceDiagram
|
|
479
|
+
participant U as Пользователь
|
|
480
|
+
participant EX as Exercise Component
|
|
481
|
+
participant STORE as Zustand Store
|
|
482
|
+
participant ACH as Achievement Checker
|
|
483
|
+
participant TOAST as Toast System
|
|
484
|
+
|
|
485
|
+
U->>EX: Ответ на упражнение
|
|
486
|
+
EX->>EX: Проверить правильность
|
|
487
|
+
EX->>STORE: completeExercise(sectionId, exerciseId, points)
|
|
488
|
+
|
|
489
|
+
STORE->>STORE: Добавить exerciseId в completedExercises
|
|
490
|
+
STORE->>STORE: Обновить sectionProgress
|
|
491
|
+
STORE->>STORE: totalPoints += points
|
|
492
|
+
|
|
493
|
+
alt Правильный ответ
|
|
494
|
+
STORE->>STORE: streak += 1
|
|
495
|
+
alt streak >= 3
|
|
496
|
+
STORE->>STORE: totalPoints += 5 (streak bonus)
|
|
497
|
+
end
|
|
498
|
+
else Неправильный ответ
|
|
499
|
+
STORE->>STORE: streak = 0
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
STORE->>ACH: Проверить все критерии достижений
|
|
503
|
+
ACH->>ACH: exercise_count? streak? section_complete?
|
|
504
|
+
|
|
505
|
+
alt Достижение разблокировано
|
|
506
|
+
ACH->>STORE: unlockAchievement(id)
|
|
507
|
+
STORE->>STORE: totalPoints += achievement.points
|
|
508
|
+
STORE->>TOAST: showToast("Achievement Unlocked!")
|
|
509
|
+
TOAST->>U: Toast уведомление (4 сек)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
STORE->>STORE: persist -> localStorage
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## MODULE: Deploy
|
|
518
|
+
|
|
519
|
+
### Назначение
|
|
520
|
+
|
|
521
|
+
Генерация GitHub Actions workflow для автоматического деплоя на GitHub Pages.
|
|
522
|
+
|
|
523
|
+
### Workflow файл
|
|
524
|
+
|
|
525
|
+
```yaml
|
|
526
|
+
# .github/workflows/deploy.yml
|
|
527
|
+
name: Deploy to GitHub Pages
|
|
528
|
+
|
|
529
|
+
on:
|
|
530
|
+
push:
|
|
531
|
+
branches: [main]
|
|
532
|
+
|
|
533
|
+
permissions:
|
|
534
|
+
contents: read
|
|
535
|
+
pages: write
|
|
536
|
+
id-token: write
|
|
537
|
+
|
|
538
|
+
concurrency:
|
|
539
|
+
group: "pages"
|
|
540
|
+
cancel-in-progress: true
|
|
541
|
+
|
|
542
|
+
jobs:
|
|
543
|
+
build:
|
|
544
|
+
runs-on: ubuntu-latest
|
|
545
|
+
steps:
|
|
546
|
+
- uses: actions/checkout@v4
|
|
547
|
+
- uses: actions/setup-node@v4
|
|
548
|
+
with:
|
|
549
|
+
node-version: 20
|
|
550
|
+
cache: 'npm'
|
|
551
|
+
- run: npm install
|
|
552
|
+
- run: npm run build
|
|
553
|
+
- uses: actions/upload-pages-artifact@v3
|
|
554
|
+
with:
|
|
555
|
+
path: './dist'
|
|
556
|
+
|
|
557
|
+
deploy:
|
|
558
|
+
environment:
|
|
559
|
+
name: github-pages
|
|
560
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
561
|
+
runs-on: ubuntu-latest
|
|
562
|
+
needs: build
|
|
563
|
+
steps:
|
|
564
|
+
- uses: actions/deploy-pages@v4
|
|
565
|
+
id: deployment
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## MODULE: Verification
|
|
571
|
+
|
|
572
|
+
### Назначение
|
|
573
|
+
|
|
574
|
+
Финальная проверка корректности сгенерированного проекта.
|
|
575
|
+
|
|
576
|
+
### 5 проверок
|
|
577
|
+
|
|
578
|
+
| Проверка | Метод | Критерий прохождения |
|
|
579
|
+
|---------|-------|---------------------|
|
|
580
|
+
| Import resolution | Парсинг `import` в JSX -> проверка существования файлов | Все импорты указывают на существующие файлы |
|
|
581
|
+
| Data file structure | Проверка экспортов в data/*.js | Массивы с обязательными полями (id, sectionId, type) |
|
|
582
|
+
| Component hierarchy | Трассировка App -> Pages -> Components | Все компоненты доступны через маршрутизацию |
|
|
583
|
+
| Router configuration | Проверка Route paths в App.jsx | Каждый Route ведёт на существующий компонент |
|
|
584
|
+
| Build simulation | Анализ JSX на синтаксические ошибки | Нет незакрытых тегов, сломанных выражений |
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## Архитектура данных
|
|
589
|
+
|
|
590
|
+
### Связи между сущностями
|
|
591
|
+
|
|
592
|
+
```mermaid
|
|
593
|
+
erDiagram
|
|
594
|
+
SECTION {
|
|
595
|
+
string id PK
|
|
596
|
+
string title
|
|
597
|
+
string description
|
|
598
|
+
string icon
|
|
599
|
+
int order
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
SUBSECTION {
|
|
603
|
+
string id PK
|
|
604
|
+
string sectionId FK
|
|
605
|
+
string title
|
|
606
|
+
string content
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
EXERCISE {
|
|
610
|
+
string id PK
|
|
611
|
+
string sectionId FK
|
|
612
|
+
string type
|
|
613
|
+
string title
|
|
614
|
+
object data
|
|
615
|
+
int points
|
|
616
|
+
int order
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
QUIZ_QUESTION {
|
|
620
|
+
string id PK
|
|
621
|
+
string sectionId FK
|
|
622
|
+
string question
|
|
623
|
+
array options
|
|
624
|
+
int correctAnswer
|
|
625
|
+
string explanation
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
ACHIEVEMENT {
|
|
629
|
+
string id PK
|
|
630
|
+
string title
|
|
631
|
+
string description
|
|
632
|
+
string icon
|
|
633
|
+
object criteria
|
|
634
|
+
int points
|
|
635
|
+
string rarity
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
SECTION ||--o{ SUBSECTION : contains
|
|
639
|
+
SECTION ||--o{ EXERCISE : contains
|
|
640
|
+
SECTION ||--o{ QUIZ_QUESTION : "provides questions for"
|
|
641
|
+
EXERCISE }o--|| ACHIEVEMENT : "triggers via criteria"
|
|
642
|
+
QUIZ_QUESTION }o--|| ACHIEVEMENT : "triggers via final_test"
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### Формат данных по типу упражнения
|
|
646
|
+
|
|
647
|
+
| Тип | Обязательные поля в `data` |
|
|
648
|
+
|-----|--------------------------|
|
|
649
|
+
| quiz | `question`, `options[]`, `correctAnswer`, `explanation` |
|
|
650
|
+
| flashcards | `cards[{front, back}]` |
|
|
651
|
+
| matching | `pairs[{left, right}]` |
|
|
652
|
+
| drag-to-order | `items[]`, `correctOrder[]` |
|
|
653
|
+
| command-builder | `instruction`, `parts[]`, `correctCommand[]` |
|
|
654
|
+
| scenario-game | `situation`, `choices[{text, outcome, isOptimal, points}]` |
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
658
|
+
## State management: Zustand
|
|
659
|
+
|
|
660
|
+
### Почему Zustand, а не Redux / Context / Jotai
|
|
661
|
+
|
|
662
|
+
| Критерий | Zustand | Redux Toolkit | React Context | Jotai |
|
|
663
|
+
|----------|---------|--------------|---------------|-------|
|
|
664
|
+
| Bundle size | ~1 KB | ~12 KB | 0 KB (built-in) | ~3 KB |
|
|
665
|
+
| Boilerplate | Минимальный | Умеренный | Минимальный | Минимальный |
|
|
666
|
+
| Persist middleware | Встроенный | Отдельный пакет | Вручную | Вручную |
|
|
667
|
+
| DevTools | Да | Да | Нет | Да |
|
|
668
|
+
| Re-renders | Селекторный | Селекторный | Весь контекст | Атомарный |
|
|
669
|
+
| Сложность для генерации | Низкая | Высокая | Низкая | Средняя |
|
|
670
|
+
|
|
671
|
+
**Zustand выбран потому что:**
|
|
672
|
+
1. Минимальный размер (1 KB) — критично для лёгкого SPA
|
|
673
|
+
2. Встроенный persist middleware — критично для сохранения прогресса
|
|
674
|
+
3. Минимальный boilerplate — упрощает генерацию кода
|
|
675
|
+
4. Селекторный re-render — производительность без лишних перерисовок
|
|
676
|
+
|
|
677
|
+
### Persist middleware: подробности
|
|
678
|
+
|
|
679
|
+
```javascript
|
|
680
|
+
persist(
|
|
681
|
+
(set, get) => ({
|
|
682
|
+
// state + actions
|
|
683
|
+
}),
|
|
684
|
+
{
|
|
685
|
+
name: 'edu-site-storage', // ключ в localStorage
|
|
686
|
+
version: 1, // для миграций
|
|
687
|
+
partialize: (state) => ({
|
|
688
|
+
// НЕ сохранять UI-state (toastQueue, currentSection)
|
|
689
|
+
completedExercises: state.completedExercises,
|
|
690
|
+
sectionProgress: state.sectionProgress,
|
|
691
|
+
totalPoints: state.totalPoints,
|
|
692
|
+
streak: state.streak,
|
|
693
|
+
longestStreak: state.longestStreak,
|
|
694
|
+
unlockedAchievements: state.unlockedAchievements,
|
|
695
|
+
finalTestScore: state.finalTestScore,
|
|
696
|
+
finalTestAnswers: state.finalTestAnswers,
|
|
697
|
+
}),
|
|
698
|
+
}
|
|
699
|
+
)
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
`partialize` исключает временные данные (очередь тостов, текущая секция) из персистенции. Это предотвращает проблемы при восстановлении стейта — например, toast, который должен был показаться 3 дня назад.
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
## Exercise Engine
|
|
707
|
+
|
|
708
|
+
### Как работает каждый из 6 типов
|
|
709
|
+
|
|
710
|
+
**Quiz:**
|
|
711
|
+
```
|
|
712
|
+
Рендер: вопрос + N кнопок-вариантов
|
|
713
|
+
Взаимодействие: клик по варианту
|
|
714
|
+
Валидация: selectedIndex === correctAnswer
|
|
715
|
+
Результат: правильно/неправильно + объяснение
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
**Flashcards:**
|
|
719
|
+
```
|
|
720
|
+
Рендер: карточка с текстом front
|
|
721
|
+
Взаимодействие: клик/пробел -> flip -> показать back
|
|
722
|
+
Валидация: пользователь отмечает "Знаю" / "Повторить"
|
|
723
|
+
Результат: "Знаю" = completed, "Повторить" = показать снова
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
**Matching:**
|
|
727
|
+
```
|
|
728
|
+
Рендер: две колонки элементов
|
|
729
|
+
Взаимодействие: drag left item -> drop on right item
|
|
730
|
+
Валидация: все пары совпадают с pairs[].left <-> pairs[].right
|
|
731
|
+
Результат: все верно (+10) / частично (+5) / неверно (0)
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
**DragToOrder:**
|
|
735
|
+
```
|
|
736
|
+
Рендер: перемешанный список элементов
|
|
737
|
+
Взаимодействие: drag-and-drop для перестановки
|
|
738
|
+
Валидация: currentOrder === correctOrder
|
|
739
|
+
Результат: верный порядок (+10) / 1 swap (+5) / иначе (0)
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
**CommandBuilder:**
|
|
743
|
+
```
|
|
744
|
+
Рендер: доступные части + пустая строка команды
|
|
745
|
+
Взаимодействие: клик/drag части -> добавить в строку
|
|
746
|
+
Валидация: builtCommand === correctCommand (или acceptableAlternatives)
|
|
747
|
+
Результат: точное совпадение (+10) / допустимый вариант (+5) / ошибка (0)
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
**ScenarioGame:**
|
|
751
|
+
```
|
|
752
|
+
Рендер: текст ситуации + N вариантов действий
|
|
753
|
+
Взаимодействие: клик по варианту
|
|
754
|
+
Валидация: choice.isOptimal
|
|
755
|
+
Результат: оптимальный выбор (+10) / допустимый (+5) / плохой (0)
|
|
756
|
+
Показать: outcome текст для выбранного варианта
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
## Обоснование технологического стека
|
|
762
|
+
|
|
763
|
+
### Почему Vite, а не CRA / Webpack / Parcel
|
|
764
|
+
|
|
765
|
+
| Критерий | Vite | CRA | Webpack (raw) | Parcel |
|
|
766
|
+
|----------|------|-----|--------------|--------|
|
|
767
|
+
| Скорость холодного старта | ~300ms | ~5s | ~3s | ~2s |
|
|
768
|
+
| HMR | Мгновенный | 1-3s | 1-3s | ~1s |
|
|
769
|
+
| Конфигурация | Минимальная | Zero-config | Сложная | Zero-config |
|
|
770
|
+
| TailwindCSS v4 интеграция | Нативный плагин | PostCSS | PostCSS | PostCSS |
|
|
771
|
+
| Build size | Оптимальный | Раздутый | Оптимальный | Хороший |
|
|
772
|
+
| ESM support | Нативный | Через babel | Через плагин | Нативный |
|
|
773
|
+
|
|
774
|
+
**Vite выбран потому что:** мгновенный HMR, нативная интеграция с TailwindCSS v4 через плагин, минимальная конфигурация, отличные результаты production build.
|
|
775
|
+
|
|
776
|
+
### Почему React 19
|
|
777
|
+
|
|
778
|
+
- Широкое знание Claude моделями (лучшая генерация кода)
|
|
779
|
+
- Экосистема компонентов и паттернов
|
|
780
|
+
- React 19: улучшенные Server Components (не используются, но модель хорошо знает API)
|
|
781
|
+
- JSX — естественный формат для генерации
|
|
782
|
+
|
|
783
|
+
### Почему TailwindCSS v4
|
|
784
|
+
|
|
785
|
+
- CSS-first конфигурация (`@theme` вместо `tailwind.config.js`)
|
|
786
|
+
- Нативный Vite плагин (не нужен PostCSS)
|
|
787
|
+
- Лучшая производительность (JIT by default)
|
|
788
|
+
- Меньший output (tree-shaking из коробки)
|
|
789
|
+
|
|
790
|
+
---
|
|
791
|
+
|
|
792
|
+
## SEO-архитектура
|
|
793
|
+
|
|
794
|
+
### index.html с OG-тегами
|
|
795
|
+
|
|
796
|
+
```html
|
|
797
|
+
<!DOCTYPE html>
|
|
798
|
+
<html lang="en">
|
|
799
|
+
<head>
|
|
800
|
+
<meta charset="UTF-8" />
|
|
801
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
802
|
+
<title>Learn Git Basics - Interactive Course</title>
|
|
803
|
+
|
|
804
|
+
<!-- Open Graph -->
|
|
805
|
+
<meta property="og:title" content="Learn Git Basics" />
|
|
806
|
+
<meta property="og:description" content="Master Git with interactive exercises" />
|
|
807
|
+
<meta property="og:type" content="website" />
|
|
808
|
+
<meta property="og:url" content="https://user.github.io/repo/" />
|
|
809
|
+
|
|
810
|
+
<!-- JSON-LD -->
|
|
811
|
+
<script type="application/ld+json">
|
|
812
|
+
{
|
|
813
|
+
"@context": "https://schema.org",
|
|
814
|
+
"@type": "Course",
|
|
815
|
+
"name": "Learn Git Basics",
|
|
816
|
+
"description": "Interactive course on Git fundamentals",
|
|
817
|
+
"provider": { "@type": "Organization", "name": "Edu-Site" },
|
|
818
|
+
"hasCourseInstance": {
|
|
819
|
+
"@type": "CourseInstance",
|
|
820
|
+
"courseMode": "online",
|
|
821
|
+
"courseWorkload": "PT2H"
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
</script>
|
|
825
|
+
</head>
|
|
826
|
+
<body>
|
|
827
|
+
<div id="root"></div>
|
|
828
|
+
<script type="module" src="/src/main.jsx"></script>
|
|
829
|
+
</body>
|
|
830
|
+
</html>
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
835
|
+
## Расширяемость
|
|
836
|
+
|
|
837
|
+
### Добавление нового типа упражнения
|
|
838
|
+
|
|
839
|
+
1. Создать компонент `src/components/interactive/NewType.jsx`
|
|
840
|
+
2. Добавить тип в `ExerciseRenderer.jsx`:
|
|
841
|
+
```javascript
|
|
842
|
+
const exerciseComponents = {
|
|
843
|
+
// ... existing types
|
|
844
|
+
'new-type': lazy(() => import('../interactive/NewType')),
|
|
845
|
+
}
|
|
846
|
+
```
|
|
847
|
+
3. Добавить формат данных в `references/exercise-types.md`
|
|
848
|
+
4. Обновить маппинг в `02-course-structure.md`
|
|
849
|
+
|
|
850
|
+
### Добавление кастомной темы
|
|
851
|
+
|
|
852
|
+
Отредактировать `@theme` блок в `src/app.css` — все CSS-переменные применятся ко всем компонентам автоматически через TailwindCSS v4.
|
|
853
|
+
|
|
854
|
+
### Интеграция с внешними сервисами
|
|
855
|
+
|
|
856
|
+
Архитектура приложения позволяет добавить внешние сервисы после генерации:
|
|
857
|
+
|
|
858
|
+
| Сервис | Интеграция | Модификация |
|
|
859
|
+
|--------|-----------|-------------|
|
|
860
|
+
| Аналитика (GA4) | Добавить скрипт в index.html | 1 файл |
|
|
861
|
+
| Backend API | Добавить fetch в Zustand actions | 1 файл (store) |
|
|
862
|
+
| Auth (Firebase) | Добавить provider в App.jsx | 2 файла |
|
|
863
|
+
| i18n (i18next) | Добавить provider + translations | 3+ файла |
|
|
864
|
+
|
|
865
|
+
---
|
|
866
|
+
|
|
867
|
+
## Проектные решения и компромиссы
|
|
868
|
+
|
|
869
|
+
### Почему static SPA, а не SSR/SSG?
|
|
870
|
+
|
|
871
|
+
Static SPA выбран для максимальной простоты деплоя. Один `npm run build` -> один `dist/` -> любой статический сервер. SSR (Next.js) требует серверную инфраструктуру. SSG (Astro, Gatsby) — хорошая альтернатива, но усложняет генерацию кода и добавляет зависимости.
|
|
872
|
+
|
|
873
|
+
**Компромисс:** SPA менее оптимален для SEO (контент рендерится в JS). Для образовательных сайтов, где основная аудитория приходит по прямой ссылке или через LMS, это допустимо.
|
|
874
|
+
|
|
875
|
+
### Почему HashRouter, а не BrowserRouter?
|
|
876
|
+
|
|
877
|
+
GitHub Pages не поддерживает серверный fallback для SPA-маршрутов. BrowserRouter требует `_redirects` или конфигурацию сервера. HashRouter работает на любом статическом хостинге без настройки.
|
|
878
|
+
|
|
879
|
+
**Компромисс:** URL вида `/#/section/1` менее красивый, чем `/section/1`. Для образовательного инструмента это не критично.
|
|
880
|
+
|
|
881
|
+
### Почему данные в JS-файлах, а не в JSON?
|
|
882
|
+
|
|
883
|
+
JavaScript файлы с `export` позволяют:
|
|
884
|
+
- Tree-shaking (если импортировать выборочно)
|
|
885
|
+
- Использовать функции и вычисляемые значения
|
|
886
|
+
- Типизацию через JSDoc (без TypeScript)
|
|
887
|
+
|
|
888
|
+
JSON потребовал бы `fetch()` в рантайме или дополнительного шага сборки для встраивания.
|
|
889
|
+
|
|
890
|
+
### Почему 6 типов упражнений, а не меньше или больше?
|
|
891
|
+
|
|
892
|
+
6 типов покрывают основные паттерны интерактивного обучения:
|
|
893
|
+
- Recall (Flashcards, Quiz)
|
|
894
|
+
- Construction (CommandBuilder, DragToOrder)
|
|
895
|
+
- Association (Matching)
|
|
896
|
+
- Application (ScenarioGame)
|
|
897
|
+
|
|
898
|
+
Меньше типов -> скучно. Больше типов -> сложнее генерировать корректные данные + больше компонентов = больший бандл.
|
|
899
|
+
|
|
900
|
+
### Почему localStorage, а не IndexedDB?
|
|
901
|
+
|
|
902
|
+
localStorage проще, синхронный, и объём данных прогресса мал (1-4 KB). IndexedDB оправдан при хранении медиа-контента или больших наборов данных — не наш случай.
|