feat(lessons): SVG-рисовалка как блок урока (svg-draw)

Лёгкий векторный редактор frontend/js/svg-draw.js (перо со сглаживанием, линия,
прямоугольник, эллипс, стрелка, текст, цвет/толщина/заливка, выбор/перемещение/удаление,
undo/redo, очистка) → выдаёт чистый <svg>. Хранится inline в данных блока, переоткрывается
для дорисовки.

- Новый тип блока svg-draw: палитра «Рисунок», редактор (монтирование виджета + подпись),
  превью и студенческий рендер (lesson.html) — санитизированный inline-SVG, адаптивный.
- Санитайзер frontend/js/svg-sanitize.js (UMD, общий клиент/сервер): whitelist тегов/атрибутов,
  вырезает script/foreignObject/style/image/a, on*=, href, javascript:. Без зависимостей.
- Сервер (lessonController): svg-draw в VALID_TYPES + очистка data.svg при сохранении.
- Переиспользуемо: тот же виджет пригоден для флешкарт и фигур генератора задач.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-03 20:11:04 +03:00
parent 71d94f45f1
commit ef59023546
5 changed files with 642 additions and 3 deletions
+9
View File
@@ -833,6 +833,7 @@
<script src="/js/api.js"></script>
<script src="/js/sidebar.js"></script>
<script src="/js/svg-sanitize.js"></script>
<script>
if (!LS.requireAuth()) throw new Error();
@@ -1142,6 +1143,14 @@
${d.caption ? `<div class="block-image-caption">${esc(d.caption)}</div>` : ''}
</div>`;
case 'svg-draw': {
const safeSvg = (window.SvgSanitize ? SvgSanitize.clean(d.svg || '') : '').replace('<svg ', '<svg style="max-width:100%;height:auto;display:block;margin:0 auto" ');
return `<div class="lesson-block block-image">
${safeSvg}
${d.caption ? `<div class="block-image-caption">${esc(d.caption)}</div>` : ''}
</div>`;
}
case 'divider':
return `<div class="lesson-block block-divider"><hr /></div>`;