Бесплатный интерактивный курс · aicoding.space

Из конспекта в обучающий сайт: skills-edu-site

Практический курс по @dzhechkov/skills-edu-site для того, кто впервые видит пакет. Вместе с Леной, преподавателем основ сетей, ты установишь пак навыков, разберёшь, что именно легло в проект, пройдёшь восемь шагов конвейера генератора, выберешь типы упражнений под своё содержание, поймёшь, где живут данные курса и как работает слой достижений, опубликуешь сайт на GitHub Pages и проверишь сборку перед тем, как показать его студентам.

Содержание курса

1. Что делает skills-edu-site

Пакет одной фразой: из документации — интерактивный обучающий сайт с тестами, карточками и достижениями.

Ключевая мысль: генератор обучающего сайта из документации

Лена преподаёт основы сетей и уже три года раздаёт студентам одну и ту же папку конспектов в markdown. Читают её, по её же подсчёту, человек пять из тридцати. Её вопрос звучал так же, как, возможно, и твой: «а можно из этого сделать что-то, во что захочется кликать?»

Можно, и ровно для этого существует пакет. @dzhechkov/skills-edu-site — это генератор обучающего сайта из документации: пак навыков для Claude Code, который превращает конспекты, руководства и базы знаний в интерактивное одностраничное приложение с тестами, карточками, достижениями, полосой прогресса и готовой публикацией на GitHub Pages.

Разберём слова, потому что дальше они будут встречаться постоянно:

  • Навык (skill) — папка с инструкциями для Claude Code: прочитав её, ассистент умеет выполнять новую задачу. Пакет ставит ровно один навык — edu-site-generator.
  • Одностраничное приложение (SPA) — сайт, который загружается один раз, а дальше переключает разделы без перезагрузки страницы. Именно такой сайт получит Лена.
  • Геймификация — очки, значки-достижения и полоса прогресса, которые превращают чтение в прохождение.

Где пакет живёт: страница на npm, исходники — в репозитории на GitHub. Пакет входит в экосистему Keysarium — о соседстве с ней будет отдельная секция.

Что важно понять с первой минуты: генератор не пишет сайт «в общих чертах». Он идёт по фиксированному конвейеру из восьми шагов — от разбора твоих документов до проверки сборки — и после каждого шага останавливается и спрашивает «ok?». Лена сначала решила, что это лишняя церемония. К концу курса ты поймёшь, почему она передумала.

Компромисс: ты получаешь проверенный маршрут от документа до сайта — но именно маршрут, а не волшебную кнопку. Содержание остаётся твоим, и качество сайта не превысит качество конспектов, которые ты в него подал.

2. Установка за одну команду

npx init, открыть Claude Code, вызвать /edu-site-generator — три шага до первого сайта.

Ключевая мысль: команда init устанавливает пак

Хватит вводных — Лена уже открыла терминал в папке своего проекта. Открывай и ты.

Путь до первого сайта — три шага:

  1. Установи пак навыков: npx @dzhechkov/skills-edu-site init. Команда init устанавливает пак в текущий проект. Голый вызов npx @dzhechkov/skills-edu-site без слова init делает то же самое — init подразумевается по умолчанию, и вопросов команда не задаёт.
  2. Открой Claude Code в этой же папке. Навык регистрируется под именем своей папки — edu-site-generator.
  3. Вызови навык: /edu-site-generator ./docs/ — и генератор начнёт разбирать твои документы.

Есть и глобальный вариант: npm install -g @dzhechkov/skills-edu-site, а потом skills-edu-site init в нужном проекте. Лена выбрала npx — ничего не остаётся в системе, кроме файлов в проекте.

Два флага, которые стоит знать до первого запуска:

  • init --dry-run — покажет, что будет сделано, ничего не записывая. Лена запустила его первым и увидела список файлов до того, как они появились.
  • init --force — перезапишет существующие файлы. Без него повторный init в уже установленный проект честно откажет: «уже установлено, используй update или --force» — и выйдет с кодом 1.

Грабли Лены — не повторяй: она первый раз запустила init не в папке проекта, а в домашней. Пакет кладёт файлы относительно текущей директории, а не «куда-нибудь в проект». Сначала cd в проект, потом init.

Что нужно на машине: Claude Code, Node.js не ниже 16 и npm — он понадобится позже, чтобы собрать сгенерированный сайт.

Компромисс: npx не оставляет следов и всегда тянет свежую версию пакета, но каждый запуск идёт в сеть; глобальная установка работает без сети, зато со временем устаревает — и в секции про update ты увидишь, чем это оборачивается.

3. Что легло в проект: навык и манифест

Папка .claude/skills/edu-site-generator/ с восемью модулями и четырьмя справочниками — плюс манифест в корне, без которого остальные команды слепы.

Ключевая мысль: манифест установки .skills-edu-site.json

Лена не любит, когда что-то «само появилось». Поэтому после init она первым делом открыла дерево проекта — давай посмотрим вместе.

В проекте появилась папка .claude/skills/edu-site-generator/, и внутри неё ровно то, что перечисляет README:

  • SKILL.md — сам навык, он же оркестратор: описывает, когда навык включается и в каком порядке читать модули.
  • modules/8 модулей, шаги 0007: от разбора содержания до проверки сборки. Каждый шаг конвейера — один файл.
  • references/4 справочника: шаблоны компонентов, схемы данных, каталог упражнений, описание стека.
  • examples/1 пример структуры готового курса на пять секций.

И ещё один файл, не в папке навыка, а в корне проекта: .skills-edu-site.json. Это манифест установки — список того, что пакет положил, с версией и датой. Лена чуть не удалила его как мусор. Не делай так: команды update, remove, list и doctor читают именно манифест, чтобы понять, что установлено. Удалишь манифест — и list скажет «пак не установлен», хотя папка навыка на месте.

Зачем манифест, если можно просто посмотреть папку? Потому что папка .claude/skills/ общая: рядом могут лежать навыки других пакетов. Манифест отвечает на вопрос «какие из этих файлов — мои», и только по нему remove может убрать своё, не тронув чужого.

Компромисс: отдельный файл-манифест — это ещё один артефакт в корне проекта, который надо коммитить вместе с навыком; взамен каждая команда пакета точно знает свою границу и никогда не удалит соседский навык.

4. Жизненный цикл пака: update, remove, list, doctor

Как обновлять, проверять и удалять пак — и почему update из старой глобальной установки ничего не обновит.

Ключевая мысль: update сверяет с шаблонами запущенной версии

Через месяц Лена услышала от коллеги: «в новой версии пакета поправили модуль проверки». Она запустила skills-edu-site update из своей глобальной установки — и увидела «всё актуально». Обновления не произошло. Прежде чем читать дальше, реши: пакет соврал, обновление ещё не вышло — или Лена спросила не у того?

Ответ — третье, и это главное, что нужно знать про update. Команда update сверяет установленные файлы с шаблонами, которые лежат внутри той версии пакета, которую ты запустил. В реестр npm она не ходит и «есть ли что новее» не проверяет. Устаревшая глобальная установка предложит только саму себя. Поэтому обновляться надо так: npx @dzhechkov/skills-edu-site@latest update@latest заставляет npx взять свежую версию, и уже её шаблоны становятся эталоном. update --dry-run покажет, какие файлы добавятся и изменятся, прежде чем что-то трогать.

Остальные команды жизненного цикла:

  • list — таблица установленных компонентов, версия и дата установки. Всё это из манифеста.
  • doctor — шесть проверок здоровья: все файлы манифеста на месте; папка навыка и SKILL.md есть; модули есть; справочники есть; пример есть; состояние интеграции с Keysarium. К каждой провалившейся проверке — подсказка, как чинить (обычно — update).
  • remove — удаляет папку навыка и манифест, но сначала спрашивает подтверждение. Если пак не установлен, просто сообщает об этом.

Про remove есть измеренная деталь, которая важна для скриптов. Когда команда запущена не из терминала, а из конвейера или пайпа (ввод — не TTY), спросить она не может. Без --force она отказывает и выходит с кодом 1, а не делает вид, что всё удалила: README приводит воспроизводитель — printf '' | node bin/cli.js remove; echo $? даёт 1 и все файлы на месте; с --force — удаляет и выходит с 0. Лена оценила это как сетевик: тихий «успех» без действия хуже громкого отказа.

Компромисс: отсутствие сетевой проверки делает update быстрым и предсказуемым — эталон всегда на диске, — но перекладывает на тебя обязанность запускать его через @latest; отказ remove без TTY защищает автоматизацию от ложного зелёного, но требует явного --force в скриптах.

5. Соседство с Keysarium

Пакет работает сам по себе, но узнаёт Keysarium в проекте — по одному файлу, без сети.

Ключевая мысль: автоопределение Keysarium по файлу .keysarium.json

В README дважды упоминается Keysarium, и Лена спросила: «мне это обязательно?» Короткий ответ — нет. Длинный — интереснее.

Keysarium — соседний пакет того же автора, большой набор навыков для Claude Code, в котором есть свой исследовательский конвейер Casarium. skills-edu-site работает и без него: всё, что ты прошёл в прошлых секциях, ни разу не потребовало Keysarium.

Но если Keysarium в проекте уже стоит, генератор об этом узнает — и вот как. При init пакет проверяет, есть ли в корне проекта файл .keysarium.json — манифест Keysarium, точно такой же по назначению, как наш .skills-edu-site.json. Нашёл — печатает рамку «Keysarium обнаружен», показывает его версию и сообщает, что оба пака делят одну папку .claude/skills/. Это и есть автоопределение Keysarium по файлу — никакого запроса в сеть, никакого сканирования системы: один файл в корне.

Лена, привыкшая к тому, что «обнаружение» обычно означает опрос сервисов, удивилась простоте. Здесь решение честнее: файл-манифест либо есть, либо нет, и результат воспроизводим на любой машине.

Что даёт соседство:

  • навык edu-site-generator становится частью общего инструментария Keysarium;
  • генератор умеет делать обучающий сайт из исследовательских артефактов, которые производит конвейер Casarium, — а не только из твоих конспектов;
  • doctor включает отдельную проверку состояния этой интеграции.

Порядок установки, если хочешь обоих: сначала npx @dzhechkov/keysarium init, потом npx @dzhechkov/skills-edu-site init — тогда второй увидит манифест первого. Поставишь в обратном порядке — ничего не сломается, просто рамки «обнаружен» при init не будет.

Компромисс: определение по файлу дёшево и воспроизводимо, но и наивно: если манифест Keysarium остался от удалённой установки, пакет всё равно скажет «обнаружен» — он верит файлу, а не проверяет соседа.

6. Восемь шагов конвейера

От разбора содержания до проверки сборки: что делает каждый шаг и почему после каждого — остановка.

Ключевая мысль: восемь шагов конвейера

Лена набирает /edu-site-generator ./docs/ — и вместо «сайт готов» получает: «Шаг 0/7: анализ содержания завершён. Тем найдено: 9. Язык: ru. «ok» — продолжить, или напиши, что поправить». Она озадачена. Ты — уже нет, потому что знаешь из первой секции: генератор идёт по конвейеру.

Конвейер — это восемь шагов, по одному файлу-модулю на каждый, modules/00-…modules/07-…:

  1. 00 Анализ содержания — читает твои документы, выделяет темы, определяет язык, прикидывает число секций.
  2. 01 Структура курса — раскладывает темы по секциям от простого к сложному и назначает каждой тип упражнения.
  3. 02 Генерация данных — пишет файлы данных: секции, упражнения, вопросы тестов, достижения.
  4. 03 Каркас проектаpackage.json, конфигурация Vite, index.html, тема в CSS.
  5. 04 Компоненты — интерфейс: раскладка, шесть интерактивных компонентов, страницы.
  6. 05 Геймификация — хранилище состояния, прогресс, достижения, уведомления.
  7. 06 Публикация — конфигурация GitHub Pages и рабочий процесс GitHub Actions.
  8. 07 Проверкаnpm run build и перекрёстные проверки.

Три взгляда на один и тот же конвейер — выбери тот, что тебе ближе:

  • Общая картина: первые три шага — про содержание, следующие три — про код, последние два — про доставку.
  • По шагам: каждый модуль заканчивается контрольной точкой «Step N/7 … Complete», списком артефактов и вопросом «ok?».
  • Как артефакт: после шага 02 у тебя есть файлы данных, которые можно открыть и прочитать ещё до того, как появится хоть один компонент.

Зачем остановка после каждого шага? Лена поняла на шаге 01: генератор назначил её теме «команды диагностики» карточки, а она хотела конструктор команд. Одно слово в ответ — «шаг 3 сделай builder» — и структура поправлена до того, как написан код. Без контрольной точки она узнала бы об этом на готовом сайте.

Компромисс: восемь остановок удлиняют путь и требуют твоего присутствия, зато каждая ошибка ловится на самом дешёвом шаге — в структуре, а не в сгенерированном коде.

7. Шесть типов упражнений — и какой к чему

Тест, карточки, сопоставление, порядок, конструктор команд, сценарий: как генератор выбирает тип по природе содержания.

Ключевая мысль: шесть типов упражнений

На шаге 01 генератор показал Лене таблицу: тема — тип упражнения. Она посмотрела и спросила: «а по какому принципу?» Принцип есть, и он записан в справочнике references/exercise-catalog.md.

В пакете шесть типов упражнений, и у каждого — своя природа содержания:

  • Тест (quiz) — факты, правила, лучшие практики. Вопросы с вариантами и объяснением к правильному ответу.
  • Карточки (flashcards) — термины и определения, всё, что запоминают парами. Карточка переворачивается по клику.
  • Сопоставление (matching) — связи: инструмент — назначение, причина — следствие. Левая колонка на месте, правая перемешана.
  • Порядок (drag-and-drop) — процессы с определённой последовательностью шагов. Перетаскиваешь, потом «Проверить».
  • Конструктор команд (builder) — синтаксис команд и вызовов, где важен порядок частей. Собираешь из кусочков, подсказки идут по нарастающей.
  • Сценарий (scenario) — решения в ситуации: несколько шагов, у каждого два-три варианта с оценкой «хорошо / нейтрально / плохо».

Значения interactiveType — ровно эти шесть слов. Старые имена ordering, simulation и drag-order модуль анализа прямо запрещает: они остались как псевдонимы в компонентах, но в новых данных им не место.

Теперь правило, ради которого эта секция цитирует принцип разнообразия. У генератора есть запрет на три одинаковых типа подряд и требование использовать все шесть типов, если секций шесть и больше. Лена спросила, зачем это в коде, а не в рекомендациях. Ответ в таблице анти-паттернов навыка: «все упражнения одного типа — монотонный интерфейс». Внимание студента устаёт от однообразия быстрее, чем от сложности. Каталог даже даёт раскладку: для 10–15 секций — 3 теста, 2 карточек, 2 сопоставления, 2 порядка, 1 конструктор, 2 сценария.

Посмотри на этот курс: тринадцать секций, шесть типов, и нигде три подряд одинаковых. Это не совпадение — это то же правило.

Компромисс: привязка типа к природе содержания даёт осмысленные упражнения, но не всякая тема ложится на «свой» тип идеально; тогда генератор выбирает ближайший, а ты правишь его на контрольной точке шага 01.

8. Ты — методист: сколько секций и из чего

Четыре формы входа, границы 3–20 секций, 150–400 слов теории: реши за Лену, как разложить её конспекты.

Ключевая мысль: от трёх до двадцати секций

В этой секции решения принимаешь ты. Лена принесла папку: 14 файлов markdown про основы сетей, два из них — по абзацу, один — на сорок страниц. Генератор ждёт от тебя не «ok», а методических решений. Правила, в которых ты их принимаешь, записаны в SKILL.md и модулях 00–01.

Что можно подать на вход — одно из четырёх:

  1. адрес документации в сети — генератор её загрузит;
  2. путь к файлу или папке — прочитает напрямую;
  3. вставленный текст — возьмёт как есть;
  4. описание темы словами — сгенерирует содержание из знаний модели.

Четвёртый способ — самый соблазнительный и самый рискованный: содержание будет правдоподобным, но не твоим. Лена отказалась от него сразу: её студенты сдают её экзамен, а не экзамен модели.

Сколько секций. Границы жёсткие: от трёх до двадцати секций. Меньше трёх — навык предупредит, что содержания не хватает для геймификации, и предложит добавить документов. Больше двадцати — предложит сгруппировать темы или разбить на два курса. Золотая середина по модулю анализа — 5–15.

Сколько теории в секции. Модуль структуры просит 150–400 слов: контекст, суть, применение. Анти-паттерн «теория слишком длинная» — больше 500 слов — лечится разбиением на подсекции. Сорокастраничный файл Лены — это не одна секция, а четыре-пять.

Что ещё решается на шаге 01:

  • порядок от простого к сложному, синтез в конце;
  • тип упражнения по природе содержания (прошлая секция);
  • отдельная эмодзи-иконка на секцию, без повторов;
  • по одному вопросу финального теста на секцию, четыре варианта.

Вопрос без единственно верного ответа, над которым Лена думала дольше всего: два файла по абзацу — это две тонкие секции, одна общая или материал для FAQ? Правильного ответа нет; есть решение, которое ты сможешь объяснить студентам.

Компромисс: жёсткие границы 3–20 и 150–400 слов отсекают заведомо плохие курсы, но внутри границ генератор не знает твоих студентов — тонкую резку тем делаешь ты, на контрольных точках шагов 00 и 01.

9. Где живёт содержание: src/data

Секции, упражнения, вопросы и достижения — в файлах данных, а не в компонентах. И почему дерево в README — эскиз, а схема — контракт.

Ключевая мысль: данные курса живут в src/data

Сайт сгенерирован. Лена нашла опечатку в вопросе теста и спросила: «где править — в компоненте теста?» Подумай сам, прежде чем читать: если содержание лежит в компонентах, сколько файлов придётся открыть, чтобы поправить одну опечатку в курсе на тринадцать секций?

Ответ генератора однозначен, и он записан в таблице анти-паттернов навыка: «содержание в JSX вместо data/» — ошибка; всё содержание обязано жить в файлах данных. Компоненты только показывают то, что в них подали. Поэтому данные курса живут в src/data, и опечатку Лена правит в одном файле, не открывая ни одного компонента.

Что лежит в src/data по обязывающему справочнику references/data-schemas.md:

  • sections.js — массив секций: id, порядок, заголовок, иконка, тип упражнения, теория; рядом TOTAL_SECTIONS.
  • exercises.js — данные упражнений по типам: карточки, пары сопоставления, порядок, конструктор, сценарии — и faqData для FAQ.
  • quizQuestions.js — вопросы тестов по секциям и finalTestQuestions: по одному на секцию, с sectionId.
  • achievements.js — достижения с условиями открытия.

А теперь честное наблюдение, которое Лена сделала раньше меня. В README, в разделе «Output Structure», дерево проекта нарисовано иначе: src/data/modules.json, exercises.json, stores/useProgress.js. В SKILL.md и схемах — sections.js, store/useAppStore.js, hooks/. Два документа одного пакета расходятся. Что считать правдой?

Правило простое: обязывающий документ — тот, по которому генератор пишет файлы, а это data-schemas.md и модуль 02: именно их навык читает на шаге генерации данных. Дерево в README — эскиз для первого взгляда. Если сомневаешься, открой сгенерированный проект: он и есть окончательный ответ.

Ещё одно, что README говорит верно: файла tailwind.config.js в проекте нет и не будет — об этом в секции про стек.

Компромисс: разнесение содержания и компонентов делает правку курса дешёвой и безопасной, но обязывает соблюдать схему: поле не по имени — и компонент молча покажет пустоту.

10. Слой достижений и прогресса

Хранилище Zustand с сохранением в localStorage, пять стандартных достижений, всплывающие уведомления и финальный тест на 70%.

Ключевая мысль: достижения и прогресс хранятся в localStorage

Студент Лены закрыл вкладку на шестой секции, а назавтра открыл сайт — и прогресс на месте: шесть галочек, два значка, полоса на 46%. Лена спросила: «где это хранится, если у нас нет сервера?»

Ответ — в браузере самого студента. Достижения и прогресс хранятся в localStorage, а управляет ими хранилище состояния Zustand с промежуточным слоем persist. Zustand — маленькая библиотека, которая держит состояние приложения в одном объекте; persist при каждом изменении записывает выбранные поля в localStorage под ключом вида <имя-курса>-progress и восстанавливает их при открытии сайта.

Что именно сохраняется (модуль 05, partialize):

  • пройденные секции и балл за каждую (0–100 по результату упражнения);
  • открытые достижения;
  • результат финального теста и ответы;
  • выбор тёмной темы — при загрузке класс dark восстанавливается ещё до первого кадра.

Достижения живут в src/data/achievements.js. Пять стандартных есть в каждом курсе: first-step (первая секция), halfway (половина), perfectionist (секция на 100), full-course (все секции), test-passed (финальный тест на 70 и выше). К ним — три-пять тематических, привязанных к группам секций; всего 8–12. Хук useAchievements после каждого изменения состояния проверяет условия и открывает новые; всплывающее уведомление появляется справа сверху и само исчезает через четыре секунды.

Финальный тест — по одному вопросу на секцию, порог прохождения 70%. Результат считается отдельно от баллов секций.

Теперь вопрос на самонаблюдение. Ты только что прошёл девять секций этого курса и, скорее всего, видел уведомления о своих значках. Вспомни момент, когда всплыло первое, — заметил ли ты, что после него прошёл следующую секцию внимательнее? Это и есть механизм, который Лена включает для своих студентов: прогресс, который видно, меняет поведение.

Компромисс: хранение в браузере не требует сервера и работает с GitHub Pages, где сервера и нет, — но прогресс привязан к одному браузеру на одном устройстве, и «сброс прогресса» — это кнопка resetProgress, а не восстановление из резервной копии.

11. Стек: React 19, Vite, TailwindCSS v4

Пять технологий сгенерированного сайта — и две «примечания мелким шрифтом», которые на деле главное: нет tailwind.config.js и только HashRouter.

Ключевая мысль: TailwindCSS v4 настраивается в CSS через @theme

Лена захотела перекрасить сайт в цвета колледжа и полезла искать tailwind.config.js. Файла нет. Она решила, что генератор его забыл, и почти создала свой. Хорошо, что дочитала README до примечания под таблицей.

Сначала таблица стека — пять технологий, из которых собран сгенерированный сайт:

  • React 19 — библиотека интерфейса.
  • Vite — сборщик и сервер разработки.
  • TailwindCSS v4 — CSS-каркас с утилитарными классами.
  • Zustand — хранилище состояния (прошлая секция).
  • React Router v7 — переключение страниц на стороне браузера.

Плюс @dnd-kit для перетаскивания в упражнениях «порядок» и «сопоставление».

А теперь два примечания, которые обычно пропускают, — и которые в этом пакете важнее таблицы. Здесь и цитируется принцип «врезки — не украшение»: то, что напечатано мелким шрифтом под таблицей, и есть главное.

Примечание первое. Файла tailwind.config.js нет, потому что TailwindCSS v4 настраивается в CSS через директиву @theme. Цвета, шрифты и прочие токены темы объявляются прямо в src/index.css; свои утилиты — через @utility, а не через @layer utilities; подключение — одной строкой @import "tailwindcss". Лена перекрасила сайт, поправив несколько строк в index.css, и никакой конфигурационный файл ей не понадобился. Создай она tailwind.config.js — он бы просто молча ничего не делал.

Примечание второе. Маршрутизатор — обязательно HashRouter, не BrowserRouter. GitHub Pages отдаёт index.html только по корню и не умеет отвечать на пути вроде /section-3 — сервера, который перехватил бы такой запрос, там нет. HashRouter кладёт раздел после #, и браузер никогда не спрашивает сервер о нём. По той же причине навигация по секциям сделана через scrollIntoView(), а не через ссылки <a href="#section-…"> — они конфликтуют с хеш-маршрутизацией.

Компромисс: TailwindCSS v4 убирает конфигурационный файл и держит тему рядом со стилями — удобно, но всё, что ты знал о tailwind.config.js из v3, здесь не работает; HashRouter даёт совместимость с GitHub Pages ценой символа # в адресах.

12. Публикация на GitHub Pages

deploy.yml, base по имени репозитория, HashRouter, push в main — и почему 404 на Pages почти всегда про base.

Ключевая мысль: base равен имени репозитория

Сайт собран, Лена запушила его в репозиторий networks-101 — и получила белую страницу с ошибками 404 в консоли. Посмотри на схему под текстом: путь до работающей страницы — четыре узла, и Лена споткнулась на втором.

Шаг 06 конвейера готовит публикацию так:

  1. .github/workflows/deploy.yml — рабочий процесс GitHub Actions: при каждом push в ветку main он ставит Node 20, выполняет npm ci и npm run build, загружает папку dist как артефакт Pages и разворачивает его. Параметр enablement: true сам включает Pages в репозитории, если они ещё не включены.
  2. base в vite.config.js — здесь и была ошибка Лены. base равен имени репозитория: для https://github.com/lena/networks-101 это base: '/networks-101/'. Генератор выводит его из адреса git remote; если удалённого репозитория ещё нет, спрашивает имя у тебя. Лена создала репозиторий после генерации, и base остался от заготовки. Все пути к скриптам и стилям смотрели не туда — отсюда 404.
  3. HashRouter — из прошлой секции: разделы после #, сервер о них не спрашивают.
  4. Push в main — и через минуту-две сайт открывается по адресу https://<пользователь>.github.io/<репозиторий>/.

Почему схема здесь важнее текста: 404 на Pages выглядит одинаково при любой из трёх причин — неверный base, BrowserRouter вместо HashRouter, не включённые Pages. Глядя на четыре узла, ты проверяешь их по очереди, а не гадаешь. Таблица анти-паттернов навыка отдельно называет «неверный base путь → 404 на GitHub Pages» и лечение: «выводить из имени репозитория».

Проверь себя: сайт по адресу https://lena.github.io/networks-101/ — какой base? Если ответил '/networks-101/' со слешами с обеих сторон — ты уже не наступишь на грабли Лены.

Компромисс: публикация через Actions полностью автоматическая и бесплатная, но привязана к имени репозитория: переименуешь репозиторий — обязан поменять base, иначе снова белая страница.

13. Проверка перед показом студентам

npm run build без ошибок, перекрёстные проверки данных и три известные ловушки — шаг 07 и честный итог курса.

Ключевая мысль: сборка npm run build подтверждает готовность

Лена собралась показывать сайт группе в понедельник. Последний шаг конвейера — 07, проверка — существует ровно для того, чтобы понедельник прошёл без сюрпризов.

Что подтверждает готовность: сборка. npm run build обязан завершиться без единой ошибки. Не «сайт открывается у меня в dev-режиме», а именно сборка: Vite в режиме разработки прощает то, что production-сборка не пропустит. Если ошибка есть, модуль 07 даёт таблицу «ошибка → причина → починка»: не найден модуль — нет файла или неверный путь; не экспортируется имя — расходятся имена в файле данных; не найден @dnd-kit — доустановить; класс TailwindCSS не применяется — токен не объявлен в @theme. Протокол: прочитать ошибку целиком, найти в таблице, починить, собрать заново — пока не станет чисто.

Перекрёстные проверки — то, что сборка не ловит, потому что это не ошибки синтаксиса, а расхождения данных:

  • у каждой секции из SECTIONS есть свой компонент SectionN.jsx;
  • у каждого interactiveType есть данные упражнения;
  • в finalTestQuestions — ровно по одному вопросу на секцию с верным sectionId;
  • файлы robots.txt и sitemap.xml на месте, и в карте сайта есть адрес каждой секции.

Три известные ловушки, которые модуль велит проверять поимённо, потому что они уже случались:

  1. В компоненте теста счётчик правильных ответов при переходе к следующему вопросу не должен прибавляться дважды.
  2. Навигация по секциям — через scrollIntoView(), не через ссылки с # (конфликт с HashRouter).
  3. Тёмная тема восстанавливается при загрузке через onRehydrateStorage.

Лена прошла всё это в субботу. Сборка упала один раз — на неверном имени экспорта в exercises.js после её правки опечатки из девятой секции. Таблица назвала причину, починка заняла минуту.

И честный итог курса, в четырёх строках, как мы закрывали каждую секцию. Сильные стороны пакета: полный маршрут от документа до опубликованного сайта, восемь контрольных точек, шесть типов упражнений, достижения без сервера. Слабые: нужен Claude Code; содержание не улучшается, только упаковывается; README местами эскиз, а не контракт; прогресс живёт в одном браузере. Оценка Лены: 4/5 — «понедельник прошёл, студенты кликали». Одной строкой: сборка npm run build подтверждает готовность — всё остальное до неё было обещанием.

Компромисс: проверка по списку занимает полчаса, которые хочется сэкономить; но каждая ловушка в списке стоила кому-то сломанного показа, и полчаса дешевле.

Частые вопросы

Пак уже установлен. Как обновиться до свежей версии?

npx @dzhechkov/skills-edu-site@latest update. Без @latest команда update сверяет файлы с шаблонами той версии, которую ты запустил, и в реестр не ходит — устаревшая глобальная установка предложит только саму себя. Сначала можно посмотреть план: update --dry-run.

Я случайно удалил .skills-edu-site.json. Что делать?

Это манифест установки — его читают update, remove, list и doctor. Папка навыка на месте, но команды её не «видят». Проще всего переустановить: npx @dzhechkov/skills-edu-site init --force — манифест будет записан заново.

Обязательно ли ставить Keysarium?

Нет. Пакет работает сам по себе. Если Keysarium стоит, init найдёт его манифест .keysarium.json в корне проекта и сообщит об интеграции; появится возможность строить сайт из артефактов конвейера Casarium.

Сколько документов нужно, чтобы генератор согласился работать?

Минимум на три секции; максимум рекомендованный — двадцать; золотая середина — 5–15. Меньше трёх — предупреждение и просьба добавить содержания, больше двадцати — предложение сгруппировать или разбить на два курса.

Где править текст и вопросы уже сгенерированного сайта?

В файлах данных src/data: sections.js (секции и теория), exercises.js (упражнения и FAQ), quizQuestions.js (тесты и финальный тест), achievements.js (достижения). Компоненты только показывают данные; обязывающая схема — references/data-schemas.md.

Почему на GitHub Pages белая страница и 404 в консоли?

Почти всегда — base в vite.config.js: он должен быть равен имени репозитория со слешами с обеих сторон, например '/networks-101/'. Вторая причина — BrowserRouter вместо HashRouter, третья — не включённые Pages (deploy.yml с enablement: true включает их сам).

Где tailwind.config.js? Его нет в проекте.

И не должно быть: сайт использует TailwindCSS v4, где тема настраивается в CSS через директиву @theme в src/index.css, а свои утилиты — через @utility. Созданный вручную tailwind.config.js ничего не сделает.

Как удалить пак из скрипта без терминала?

npx @dzhechkov/skills-edu-site remove --force. Без --force при вводе не из терминала команда откажет с кодом 1 и ничего не удалит — это измерено и описано в README, чтобы автоматизация не видела ложный успех.