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

Из записи — в читаемый сайт: курс по skills-transcript-site

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

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

1. Зачем превращать запись в сайт?

Что делает пакет одной фразой — и почему текст выигрывает у видео.

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

У Марго в облаке лежат записи докладов сообщества: девяносто минут, ещё девяносто, ещё. Смотрит их примерно никто, и целый год Марго была уверена, что дело в темах.

Дело не в темах. Видео нельзя пролистать глазами, внутри него нельзя найти слово, и поисковик не знает, о чём оно. Текст умеет всё это по своей природе — надо только превратить запись в текст и положить его туда, где живут страницы.

Ровно это делает пакет @dzhechkov/skills-transcript-site: он собирает статический сайт из транскрипта без шага сборки. Разберём обещание по словам:

  • статический — на выходе обычные файлы: docs/index.html, docs/static/app.js, docs/static/style.css; браузер открывает их как есть;
  • из транскрипта — на входе текст расшифровки или ссылка на YouTube, из которой субтитры достанут за тебя;
  • без шага сборки — ни npm run build, ни React, ни Vite: положил папку docs/ в репозиторий — сайт опубликован.

И сразу главное про удобство: команды из этого курса не надо заучивать. Внутри Claude Code Марго просто говорит: «сделай сайт из вот этой расшифровки» — и навык срабатывает сам. Команды нужны, чтобы ты понимал, что происходит под капотом.

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

2. Ставим пак навыков

Три двери установки, сухой прогон до первой записи на диск и ссылки на пакет.

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

Марго открывает терминал в папке проекта. Монтаж короткий — пакет опубликован в npm, и всё сводится к одной строке.

Дверей три, выбирай по привычке:

  1. npx @dzhechkov/skills-transcript-site — разовый запуск, ничего не остаётся в системе;
  2. npm install -g @dzhechkov/skills-transcript-site, затем skills-transcript-site init — глобальная команда, если ставишь часто;
  3. npx @dzhechkov/skills-transcript-site init в проекте, где уже есть @dzhechkov/keysarium — пак встанет рядом и будет виден вместе с остальной библиотекой навыков.

Перед любой из них сделай сухой прогон: npx @dzhechkov/skills-transcript-site init --dry-run покажет список файлов, которые появятся, и не тронет диск. Марго один раз пропустила этот шаг в чужом репозитории и потом полчаса выясняла, что именно там поменялось.

Полезные ссылки держи под рукой:

Требования скромные: Node.js не ниже 16 и настроенный Claude Code CLI. yt-dlp понадобится только для ссылок на YouTube — и только тогда.

Компромисс: установка пака навыков кладёт в твой .claude/ чужие файлы, и обновлять их придётся отдельной командой. Взамен навык работает одинаково у всей команды, а не живёт в голове одного человека.

3. Что именно легло в проект

Разбираем содержимое .claude/skills/transcript-site-generator/ по полочкам.

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

После установки Марго делает то, что стоит делать всегда: смотрит, что именно появилось. Всё лежит в одной папке проекта — .claude/skills/transcript-site-generator/.

Состав пака запоминается за один взгляд: один навык, шесть модулей, три справочника, два примера.

  1. SKILL.md — оркестратор: когда навык срабатывает, что он обещает на выходе и в каком порядке читает модули.
  2. modules/00-input-analysis.mdmodules/05-verification.md — шесть шагов конвейера, по файлу на шаг.
  3. references/tech-stack.md, references/data-schemas.md, references/component-catalog.md — справочники: чем собираем, какая структура у секции, какие блоки страницы бывают.
  4. examples/sample-transcript-site.md и examples/sample-text-only-site.md — два готовых примера: с видео и без него.

Зачем такое дробление, а не один длинный файл: модель читает ровно тот модуль, который нужен на текущем шаге. Это экономит контекст и делает поведение навыка предсказуемым — Марго может открыть modules/02-site-generation.md и увидеть в точности то, чем руководствовался генератор, когда собирал её страницу.

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

4. Восемь команд установщика

init, --force, --dry-run, update, remove, list, doctor — и когда что применять.

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

У установщика восемь команд, и Марго держит в голове только две — остальные вспоминаются по смыслу.

Полный список:

  • init — поставить все компоненты (без аргументов пакет делает то же самое);
  • init --force — перезаписать то, что уже лежит на месте;
  • init --dry-runсухой прогон: показать изменения и не тронуть диск;
  • update — обновить установленные файлы до свежей версии пакета;
  • remove — чистое удаление, без хвостов;
  • list — показать, что реально установлено сейчас;
  • doctor — проверка здоровья установки: на месте ли файлы, всё ли согласовано.

Две команды, которые стоит запомнить дословно: init --dry-run — до изменений, doctor — после. Первая отвечает на вопрос «что произойдёт», вторая — на вопрос «всё ли в порядке сейчас». Между ними живёт вся остальная жизнь пака.

Отдельно про --force. Он не спрашивает, он перезаписывает. Марго применяет его в одном случае: когда точно знает, что правила файлы навыка руками и хочет вернуть заводское состояние. Во всех остальных сначала list, потом init --dry-run, и только потом решение.

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

5. Конвейер из шести шагов

Общая карта: что происходит от входа до проверки и где тебя спрашивают.

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

Прежде чем лезть в детали, Марго рисует карту. Навык работает как конвейер из шести шагов с чекпоинтом после каждого — и это самая полезная картинка курса.

Шаги идут строго по порядку, потому что каждый следующий питается выходом предыдущего:

  1. Разбор входа — понять, что дали: текст, ссылку или оба, вытащить расшифровку и определить язык.
  2. Нарезка — разложить сплошной транскрипт на секции с заголовками и таймкодами.
  3. Сборка страницы — собрать docs/index.html со всем содержимым и SEO-разметкой.
  4. Интерактивность — сгенерировать docs/static/app.js: поиск, оглавление, тёмная тема.
  5. Публикация — положить конфигурацию GitHub Pages и README рядом.
  6. Проверка — пройти по чеклисту и сказать вслух, что сошлось, а что нет.

После каждого шага генератор останавливается и показывает чекпоинт: что сделано, какие файлы появились, и приглашение ответить «ok» или дать правку. Это не декорация. Ошибка нарезки, замеченная на втором шаге, стоит одной реплики; она же, замеченная после публикации, стоит переделки страницы целиком.

Марго проверила это на себе: на первом прогоне она проскочила чекпоинт после нарезки, а на пятом шаге обнаружила, что весь доклад уехал в одну гигантскую секцию.

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

6. Шаг 0: генератор разбирается, что ему дали

Классификация входа, добыча субтитров через yt-dlp и выбор языка интерфейса.

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

Марго приносит ссылку на девяностоминутный доклад и никакого текста. Шаг 0 обязан из этого сделать расшифровку — или честно сказать, что не может.

Сначала классификация. Генератор смотрит на вход и решает, что это:

  • youtube.com/watch?v= или youtu.be/ — ссылка на видео, расшифровку добываем;
  • путь к файлу .txt, .md, .vtt, .srt — читаем файл;
  • многострочный кусок текста длиннее ста символов — берём как есть;
  • ссылка и текст вместе — спрашиваем, а не угадываем: использовать видео только для встраивания, или вытащить расшифровку из него.

Дальше добыча субтитров. Порядок попыток жёсткий: сначала yt-dlp --write-sub (ручные субтитры, они точнее), и только если их нет — --write-auto-sub (автоматические). Если yt-dlp в системе нет, генератор не изображает успех: он говорит, что нужно поставить yt-dlp или вставить текст руками. Отсутствие квитанции — не успех.

Сырой .vtt приходится чистить: выкинуть заголовок WEBVTT, вытащить пары «таймкод + текст», убрать дубли перекрывающихся отрезков, склеить подряд идущие реплики одного спикера и вычистить служебные теги.

И наконец язык. Определение языка интерфейса идёт по преобладающему алфавиту: в первых 500 символах считаются символы по диапазонам Юникода — кириллица, латиница, иероглифы, арабица, — побеждает самый частый. Кириллица даёт русский интерфейс, латиница — английский, всё остальное — английский как запасной вариант.

Важная честная оговорка из документации: это выбор языка только для подписей интерфейса — кнопок, поиска, счётчиков. Сам текст доклада рендерится как есть. И интерфейс есть пока только на русском и английском: украинский или болгарский получат русские подписи как ближайшие доступные.

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

7. Шаг 1: как транскрипт становится секциями

Три стратегии нарезки, заголовки, спикеры и вопрос без единственного ответа.

Ключевая мысль: три стратегии нарезки: явные маркеры, смысловые сдвиги, время

Сплошная стена текста на девяносто минут не читается ни на каком экране. Задача шага 1 — превратить её в секции, по которым можно прыгать.

Генератор перебирает три стратегии нарезки по очереди, пока какая-нибудь не даст хотя бы три секции:

  1. Явные маркеры. Если в тексте уже есть структура — заголовки markdown, нумерация «1.», жирные подписи, метки спикеров «Ведущий:», разделители — берём её. Дешевле всего и точнее всего.
  2. Смысловые сдвиги. Маркеров нет — читаем абзацы и группируем по смене темы. Цель — от 5 до 15 секций, каждая на 200–2000 слов.
  3. Время. Если есть таймкоды, режем на отрезки примерно по пять минут и подвигаем границы к естественным паузам.

Заголовок для каждой секции пишется в 3–8 слов и по существу: «Часть 1» и «Продолжение» — запрещённые названия, потому что по ним нельзя выбрать, куда прыгать.

Попутно шаг 1 вытаскивает спикеров (по меткам вроде «Имя:» или «[Имя]»), проставляет таймкод начала каждой секции, считает слова и время чтения из расчёта 200 слов в минуту, а текст чистит: схлопывает лишние пробелы, чинит фразы, разорванные склейкой субтитров, и экранирует HTML-символы.

Есть и защита от крайностей: секция длиннее 3000 слов режется по границам абзацев автоматически, а если секций получилось всего одна или две — генератор предупреждает и предлагает поделить по времени.

А теперь вопрос без единственного правильного ответа, тот самый, на котором Марго застряла на полчаса: где вообще проходит граница секции? Спикер сменил тему в середине фразы — это новая секция или продолжение старой? Ответ зависит от того, как читатель будет искать: по темам — режь по смыслу; по ходу разговора — режь по времени. Машина здесь не решает за тебя, она предлагает.

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

8. Шаг 2: одна страница, которая знает о себе всё

Из чего собран index.html: стили без сборки, SEO-разметка и смысловая структура.

Ключевая мысль: самодостаточная страница index.html с полной SEO-разметкой

Шаг 2 выдаёт главный артефакт курса: самодостаточная страница index.html с полной SEO-разметкой. Самодостаточная — значит весь доклад, всё оформление и все метаданные лежат в одном файле; наружу торчат только два CDN-адреса.

Что внутри:

  • Tailwind CSS через CDN — тег script в head, и оформление работает без единой команды сборки. Рядом обязательная строка tailwind.config = { darkMode: 'class' }: без неё тёмная тема просто не включится.
  • Font Awesome через CDN — иконки луны и солнца, лупы, копирования, гамбургер-меню.
  • SEO-разметка целиком — Open Graph и Twitter Card для красивой карточки в мессенджере, плюс структурные данные JSON-LD: Article для текстового доклада и дополнительно VideoObject, если было видео.
  • Смысловые тегиheader, nav, main, article, section, footer вместо россыпи div: и поисковику понятнее, и программе чтения с экрана.
  • Содержимое — все секции доклада с уникальными якорями, боковое оглавление на широком экране и гамбургер-меню на узком, встроенное видео в контейнере с фиксированным соотношением сторон.

Марго проверила эффект самой дешёвой проверкой: кинула ссылку в чат сообщества. Раньше прилетала голая ссылка, теперь — карточка с заголовком, описанием и обложкой видео. Работает та самая разметка Open Graph, которую легко счесть необязательной.

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

9. Шаг 3: оживляем страницу без фреймворков

Поиск, оглавление, тёмная тема и одна ловушка порядка загрузки.

Ключевая мысль: интерактивность на ванильном JavaScript без фреймворков

Шаг 3 выдаёт docs/static/app.js — и вот тут курс делает заявление, которое обычно не верят с первого раза: вся интерактивность написана на ванильном JavaScript без фреймворков, в одном файле, с нулём зависимостей.

Что в этом файле живёт:

  • Поиск по всему тексту — модалка по Ctrl/Cmd+K, задержка 300 мс перед запросом, подсветка найденного и кусочек контекста вокруг;
  • Слежение за оглавлением — активный пункт подсвечивается через IntersectionObserver, без единого обработчика прокрутки;
  • Тёмная тема — переключатель с запоминанием выбора в localStorage и подхватом системной настройки при первом визите;
  • Полоса прогресса чтения — обновляется через requestAnimationFrame, а не на каждом событии прокрутки;
  • Кнопка «наверх» — появляется после 500 пикселей прокрутки;
  • Копирование цитаты — кнопка у каждой секции, обработчик один на всю страницу через делегирование по атрибуту data-copy;
  • Мобильная навигация — гамбургер, закрытие по клику вне панели и по выбору пункта.

Отдельно — ловушка, из-за которой Марго полчаса ловила «иногда не работает». Если на странице есть видео, YouTube вызывает onYouTubeIframeAPIReady, как только загрузится его скрипт, — и это может случиться раньше, чем сработает DOMContentLoaded. Поэтому обработчик регистрируется на верхнем уровне файла, до всего остального. Спрятать его внутрь DOMContentLoaded — значит получить переход по таймкодам, который работает через раз в зависимости от скорости сети.

И два обязательных предохранителя: escapeHtml не даёт тексту доклада превратиться в разметку, а escapeRegex не даёт символу из поискового запроса сломать регулярное выражение. Пользовательский текст — всегда данные, никогда не код.

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

10. Шаг 4: публикация из папки docs

Рабочий процесс GitHub Pages, два способа публикации и типичная ошибка с путём.

Ключевая мысль: публикация из папки docs без шага сборки

Сайт готов и лежит на диске. Шаг 4 отвечает на вопрос «как его увидят люди», и ответ у него один: публикация из папки docs без шага сборки.

Генератор кладёт рядом .github/workflows/deploy.yml. Что в нём важно понимать:

  1. Запуск — по отправке в ветку main и вручную через workflow_dispatch.
  2. Права — pages: write и id-token: write: без них GitHub откажет в публикации.
  3. Группа параллелизма pages с отменой предыдущего запуска — два одновременных выкладывания не подерутся.
  4. Загрузка артефакта с path: 'docs' — именно эта строка говорит, какую папку публиковать.
  5. Никакого шага сборки в рабочем процессе нет вообще: публиковать нечего собирать.

Потом одна ручная настройка в репозитории: Settings → Pages, источник «GitHub Actions». Есть и второй способ, попроще: источник «Deploy from a branch», ветка main, папка /docs — тогда рабочий процесс необязателен. Марго выбрала первый, потому что в журнале Actions видно, когда и что выложилось.

И грабли, на которые она наступила: она попросила вывести сайт в папку site/ вместо docs/, а path: 'docs' в рабочем процессе остался прежним. Публикация прошла успешно и выложила пустоту. Сменил папку вывода — поменяй путь в рабочем процессе.

Попутно шаг 4 создаёт или обновляет README.md: описание, ссылка на живой сайт, список возможностей и ссылка на исходное видео.

Компромисс: GitHub Pages бесплатен и не требует обслуживания, но привязывает тебя к GitHub и к публичной раздаче статики. Для архива докладов сообщества это ровно то, что нужно.

11. Шаг 5: проверка вместо веры

Чеклист из восьми групп и лестница проверок от самой дешёвой к самой дорогой.

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

Последний шаг существует ровно потому, что «выглядит нормально» — это не результат. Шаг 5 — проверка вывода по чеклисту, а не на веру.

Чеклист идёт по восьми группам:

  1. Файлы на местеdocs/index.html больше 10 КБ, docs/static/app.js больше 5 КБ, style.css, deploy.yml, README.md существуют.
  2. Структура страницы — объявление типа документа, lang, кодировка, viewport, непустой заголовок, описание, Open Graph, Twitter Card, JSON-LD, подключения Tailwind, Font Awesome и app.js.
  3. Содержимое — все якоря секций уникальны, число секций совпадает с итогом шага 1, нет пустых секций и не осталось ни одного {placeholder} или [TODO].
  4. Поведение — на месте escapeHtml и escapeRegex, слежение за оглавлением через IntersectionObserver, а для видео — обработчик готовности плеера на верхнем уровне.
  5. Доступность — ссылка «к содержимому» перед шапкой, роли banner и main, подпись у бокового меню, роль диалога у поиска, доступные подписи у кнопок-иконок.
  6. Отзывчивость — гамбургер скрыт на широком экране, панель выезжает на узком, текст читается без горизонтальной прокрутки на ширине 320 пикселей.
  7. Тёмная тема — у каждого светлого класса есть парный тёмный, и конфигурация Tailwind задана.
  8. SEO и печать — заголовки в разметке совпадают, а style.css содержит правила печати.

Поверх чеклиста — две детерминированные проверки, которые ничего не стоят. node --check docs/static/app.js ловит синтаксическую ошибку до читателя. grep -c '</html>' docs/index.html обязан вернуть ровно 1: не ноль (файл оборвался) и не два (генератор дописал вторую копию). Ровно так Марго нашла обрезанный файл после большого доклада — глазами она бы этого не увидела, потому что верх страницы выглядел безупречно.

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

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

12. Грабли, которые уже собрали за тебя

Список известных ошибок и стратегия работы с очень большим транскриптом.

Ключевая мысль: антипаттерны генератора и стратегия большого транскрипта

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

Вот те, что стоят внимания:

  • React или Vite для простого доклада — лишняя сложность там, где хватает статики.
  • Доклад, разложенный по нескольким HTML-страницам — вместо одной страницы с якорями; поиск ломается, а навигация становится перезагрузкой.
  • Забытый тег Tailwind или строка darkMode — страница без оформления либо с мёртвым переключателем темы.
  • Меньше трёх секций — навигации нет, значит, весь смысл сайта потерян.
  • Секция длиннее 3000 слов — стена текста внутри страницы против стены текста.
  • Поиск без сообщения «ничего не найдено» — пустой экран читается как поломка.
  • Встроенное видео фиксированной ширины — на телефоне уезжает за край; нужен контейнер с соотношением сторон.
  • Нет запасного пути, если yt-dlp не установлен — падение вместо внятного отказа.

Отдельно — правило, которое спасает чужие репозитории: папка вывода никогда не перезаписывается молча. Если docs/ уже существует, генератор спрашивает: перезаписать, отменить или писать в другую папку.

И финальная история, ради которой Марго вообще дочитала документацию. Её самый длинный доклад дал транскрипт на сорок тысяч слов, а страница на выходе оборвалась на середине. Для таких случаев есть стратегия большого транскрипта — писать не одним куском, а в четыре приёма: сначала оболочка страницы, потом секции пачками по 5–10, потом закрывающие теги, потом проверка целостности. Порог — примерно 30 000 слов, то есть около 150 КБ готового HTML.

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

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

У меня нет YouTube-ссылки, только текстовый файл. Пакет вообще нужен?

Да, это основной сценарий. Ссылка нужна только для встроенного плеера и переходов по таймкодам. На вход принимаются .txt, .md, .srt и .vtt, и весь остальной конвейер работает одинаково.

Обязательно ли ставить yt-dlp?

Нет. yt-dlp нужен ровно в одном случае — когда вы подаёте ссылку на YouTube и хотите, чтобы генератор сам достал субтитры. Если его нет, шаг 0 честно скажет об этом и предложит вставить текст расшифровки вручную.

Как посмотреть, что установка изменит в проекте, ничего не сломав?

Запустите npx @dzhechkov/skills-transcript-site init --dry-run: это сухой прогон, он печатает список файлов и не трогает диск. После установки состояние показывает doctor, а список компонентов — list.

Почему тёмная тема не переключается?

В девяти случаях из десяти в head отсутствует строка tailwind.config = { darkMode: 'class' }. Без неё классы dark: в разметке ничего не значат. Это первый пункт в списке антипаттернов.

Я вывел сайт не в docs/, а в другую папку — почему по ссылке пусто?

Путь к папке указан ещё и в .github/workflows/deploy.yml в строке path: 'docs'. Смените папку вывода — поменяйте и его, иначе рабочий процесс успешно опубликует пустоту.

Транскрипт очень длинный, страница обрывается. Что делать?

Начиная примерно с 30 000 слов используйте стратегию большого транскрипта: сначала оболочка страницы, затем секции пачками по 5–10, затем закрывающие теги, затем проверка целостности. Контрольная команда — grep -c '</html>' docs/index.html, ответ должен быть ровно 1.

Нужно ли запоминать все команды из курса?

Нет. Внутри Claude Code достаточно попросить словами: «сделай сайт из этой расшифровки» или «покажи, что установлено». Команды в курсе нужны, чтобы вы понимали, что происходит под капотом, и могли проверить результат сами.