Главная/Документация/Справочник агента/Руководство по блокам HTML Preview
Справочник агента

Руководство по блокам HTML Preview

Как отображать HTML-контент inline с помощью блоков html-preview и готовить HTML-файлы из разных источников через transform_data.

Обзор

Блок html-preview отображает HTML-файлы в изолированных iframe — подходит для писем, рассылок, HTML-отчётов и любого контента, где конвертация в markdown приведёт к потере оформления.

ФорматЛучше всего дляОтображение
MarkdownТекстоёмкий контент, код, спискиНативный markdown
Блок html-previewПисьма, рассылки, стилизованные отчёты, богатый HTMLИзолированный iframe с полным CSS

Ключевой принцип: HTML-контент всегда привязан к файлу (ссылается через src), чтобы не вставлять большие HTML-нагрузки как токены. Типичное HTML-тело письма — 50–150 КБ — его нельзя вставлять напрямую.

Когда использовать

Используйте html-preview, если:

  • HTML-тела писем — Gmail, Outlook или любой email API возвращает HTML-контент
  • Рассылки — Substack, Mailchimp и другие сервисы используют сложные CSS-макеты, которые markdown не воспроизводит
  • HTML-отчёты — ответы API с уже отформатированным HTML (аналитические дашборды, сгенерированные отчёты)
  • Богатые документы — любой контент со сложным CSS, табличными макетами, фоновыми изображениями или кастомными шрифтами
  • Веб-контент — HTML-снимки или превью, где важна точность макета

Не используйте html-preview, если:

  • Контент — простой текст — просто выведите его в markdown
  • Контент — структурированные данные — используйте datatable или spreadsheet
  • Контент — фрагмент кода — используйте обычные блоки кода с подсветкой синтаксиса
  • HTML очень маленький (< 1 КБ) — вместо этого сделайте резюме в markdown

Базовое использование

Один элемент

terminalhtml
```html-preview
{
  "src": "/absolute/path/to/file.html",
  "title": "My HTML Content"
}
```

Несколько элементов (вкладки)

Если есть несколько связанных HTML-файлов (например, цепочка писем, несколько отчётов), используйте массив items. Под заголовком появится панель вкладок для переключения между элементами.

terminalhtml
```html-preview
{
  "title": "Email Thread",
  "items": [
    { "src": "/path/to/original.html", "label": "Original" },
    { "src": "/path/to/reply.html", "label": "Reply" },
    { "src": "/path/to/forward.html", "label": "Forward" }
  ]
}
```

Контент загружается лениво при переключении вкладки и кэшируется после загрузки.

Поля конфигурации

ПолеОбязательноТипОписание
srcДа*stringАбсолютный путь к HTML-файлу на диске (режим одного элемента)
titleНетstringОтображаемый заголовок в верхней панели (по умолчанию "HTML Preview")
itemsДа*arrayМассив элементов с src и необязательным label (режим нескольких элементов)
items[].srcДаstringАбсолютный путь к HTML-файлу
items[].labelНетstringПодпись вкладки (по умолчанию "Item 1", "Item 2" и т. д.)

*Требуется либо src (один), либо items (несколько). Если оба присутствуют, приоритет у items.

Важно: путь src должен быть абсолютным — используйте точный путь, возвращённый transform_data, или соберите его с использованием пути к папке данных сессии.

Подготовка HTML-контента

Использование transform_data

Инструмент transform_data — основной способ извлекать и записывать HTML-файлы. Он запускает скрипт, который читает входные файлы и записывает результат.

Ключевое отличие от использования datatable: для html-preview выходной файл — .html (не .json). Скрипт записывает исходный HTML-контент, а не JSON.

Параметры:

ПараметрТипОписание
language"python3" | "node" | "bun"Среда выполнения скрипта
scriptstringИсходный код скрипта преобразования
inputFilesstring[]Пути входных файлов относительно папки сессии
outputFilestringИмя выходного файла, заканчивающееся на .html (записывается в папку data/ сессии)

Соглашения по путям:

  • Входные файлы — относительно папки сессии. Частые расположения:
    • long_responses/tool_result_abc.txt — сохранённые результаты инструментов (ответы Gmail API и т. п.)
    • data/previous_output.html — результат предыдущего преобразования
  • Выходной файл — относительно папки data/ сессии. Достаточно указать имя файла (например, "email.html")

Использование инструмента Write

Для небольших HTML-контента (сгенерированные отчёты, простой HTML) можно напрямую использовать инструмент Write, чтобы записать .html файл в папку данных сессии, а затем сослаться на него.

Частые паттерны и рецепты

Отображение писем Gmail

Gmail API возвращает тела писем как строки, закодированные в base64url. HTML-тело обычно находится в payload.parts[1].body.data для multipart-писем.

Надёжный паттерн (обрабатывает все MIME-структуры):

terminalpython
import base64, json, sys

with open(sys.argv[1]) as f:
    msg = json.load(f)

# Рекурсивно найти часть text/html в MIME-структуре
def find_html_part(payload):
    if payload.get('mimeType') == 'text/html':
        return payload.get('body', {}).get('data')
    for part in payload.get('parts', []):
        result = find_html_part(part)
        if result:
            return result
    return None

html_b64 = find_html_part(msg['payload'])
if not html_b64:
    # Запасной вариант: само тело может быть HTML (письма без multipart)
    html_b64 = msg['payload'].get('body', {}).get('data', '')

# Gmail использует URL-safe base64
html = base64.urlsafe_b64decode(html_b64).decode('utf-8')

with open(sys.argv[-1], 'w') as f:
    f.write(html)

Вызов:

terminalbash
transform_data({
  language: "python3",
  script: "...",
  inputFiles: ["long_responses/gmail_message.txt"],
  outputFile: "email.html"
})

Простой вариант (если структура известна):

terminalpython
import base64, json, sys
data = json.load(open(sys.argv[1]))
html = base64.urlsafe_b64decode(data['payload']['parts'][1]['body']['data']).decode('utf-8')
open(sys.argv[-1], 'w').write(html)

Письма Microsoft Outlook

Outlook / Microsoft Graph API возвращает тела писем иначе:

terminalpython
import json, sys

with open(sys.argv[1]) as f:
    msg = json.load(f)

# Microsoft Graph возвращает HTML в body.content
html = msg.get('body', {}).get('content', '')

with open(sys.argv[-1], 'w') as f:
    f.write(html)

HTML из ответов API

Многие API возвращают HTML-контент в JSON-поле:

terminalpython
import json, sys

with open(sys.argv[1]) as f:
    data = json.load(f)

# Подставьте имя поля под ваш API
html = data.get('html_content', data.get('body_html', data.get('html', '')))

with open(sys.argv[-1], 'w') as f:
    f.write(html)

Сгенерированный HTML-отчёт

Соберите HTML-отчёт из структурированных данных:

terminalpython
import json, sys

with open(sys.argv[1]) as f:
    data = json.load(f)

items = data.get('items', data.get('data', []))

rows_html = ''.join(
    f'<tr><td>{item["name"]}</td><td>${item["amount"]:,.2f}</td></tr>'
    for item in items
)

html = f"""<!DOCTYPE html>
<html>
<head>
<style>
  body {{ font-family: -apple-system, BlinkMacSystemFont, sans-serif; padding: 24px; }}
  table {{ border-collapse: collapse; width: 100%; }}
  th, td {{ padding: 8px 12px; border-bottom: 1px solid #eee; text-align: left; }}
  th {{ font-weight: 600; color: #666; }}
</style>
</head>
<body>
<h2>Report</h2>
<table>
<thead><tr><th>Name</th><th>Amount</th></tr></thead>
<tbody>{rows_html}</tbody>
</table>
</body>
</html>"""

with open(sys.argv[-1], 'w') as f:
    f.write(html)

Альтернатива на Node.js

terminaljavascript
const fs = require('fs');
const data = JSON.parse(fs.readFileSync(process.argv[2], 'utf-8'));

// Извлечь HTML из письма Gmail
const html = Buffer.from(data.payload.parts[1].body.data, 'base64url').toString('utf-8');

fs.writeFileSync(process.argv.at(-1), html);

Полный пример рабочего процесса

Пользователь спрашивает: «Покажи мне ту рассылку от Scott Belsky»

Шаг 1: Найдите письмо в Gmail:

terminalbash
GET gmail/v1/users/me/messages?q=from:scott belsky subject:implications

Шаг 2: Получите полное сообщение:

terminalbash
GET gmail/v1/users/me/messages/{id}?format=full

Шаг 3: Вызовите transform_data, чтобы декодировать HTML-тело:

terminalbash
transform_data({
  language: "python3",
  script: "import base64, json, sys\nwith open(sys.argv[1]) as f:\n    msg = json.load(f)\ndef find_html(p):\n    if p.get('mimeType')=='text/html': return p['body']['data']\n    for part in p.get('parts',[]): \n        r=find_html(part)\n        if r: return r\nhtml=base64.urlsafe_b64decode(find_html(msg['payload'])).decode('utf-8')\nopen(sys.argv[-1],'w').write(html)",
  inputFiles: ["long_responses/gmail_result.txt"],
  outputFile: "newsletter.html"
})

Шаг 4: Выведите блок html-preview с абсолютным путём из результата transform_data:

terminalhtml
```html-preview
{
  "src": "/absolute/path/from/transform_data/newsletter.html",
  "title": "Implications #40 — Exponential Code, Network Effects In AI"
}
```

Поведение при отображении

Inline-превью

  • Фиксированный max-height 400px с нижним градиентом затухания, указывающим на наличие контента ниже
  • Кнопка развернуть (в правом верхнем углу, видна при наведении) открывает полноэкранный вид
  • Верхняя панель показывает иконку Globe и заголовок

Полноэкранный оверлей

  • Нажмите кнопку развернуть для отображения на всю высоту с прокручиваемым контентом
  • Кнопка Copy HTML копирует исходный HTML в буфер обмена
  • Бейдж "HTML" в заголовке указывает тип контента

Визуальные детали

  • Белый фон — iframe отображаются с белым фоном (стандарт для HTML-писем/документов)
  • Внешние изображения — загружаются из исходных URL (CSP поддерживает https://)
  • CSS-стили — работают все inline- и встроенные стили (без ограничений на внешние таблицы стилей)
  • Адаптивные макеты — если в HTML есть адаптивный CSS, он подстраивается под ширину iframe

Рекомендации для писем

Поиск HTML-части

MIME-структуры писем различаются. Частые паттерны:

СтруктураГде находится HTML
multipart/alternativepayload.parts[1].body.data (обычно индекс 1 — HTML)
multipart/mixedmultipart/alternativepayload.parts[0].parts[1].body.data
Одночастный HTMLpayload.body.data (нет массива parts)
Только текстовое письмоНет HTML-части — используйте markdown

Всегда используйте рекурсивный паттерн find_html_part() из рецепта Gmail выше — он надёжно обрабатывает все структуры.

Base64-кодирование Gmail

Gmail использует URL-safe base64 (RFC 4648 §5):

  • Используются - и _ вместо + и /
  • Нет выравнивания (=)
  • Python: base64.urlsafe_b64decode() обрабатывает это
  • Node: Buffer.from(data, 'base64url')

Не используйте стандартный base64.b64decode() — он не сработает для URL-safe-контента.

Крупные письма

Некоторые HTML-тела рассылок превышают 100 КБ. Это нормально:

  • transform_data записывает на диск (без затрат токенов)
  • Iframe загружает файл напрямую
  • Inline-превью 400px показывает только верхнюю часть

Безопасность

HTML отображается в изолированном iframe со следующими ограничениями:

ВозможностьСтатусДетали
Выполнение JavaScriptЗаблокированоАтрибут sandbox без allow-scripts
Отправка формЗаблокированоНет allow-forms
Навигация по ссылкамЗаблокированоSandbox блокирует всю навигацию
Всплывающие окна / новые окнаЗаблокированоНет allow-popups
CSS-стилиРазрешеноРаботают inline-, встроенные и теги <style>
Изображения (https://)РазрешеноВнешние изображения загружаются нормально
Изображения (data:)РазрешеноBase64-изображения работают
Встроенные шрифтыРазрешеноЗагружаются Google Fonts и другие шрифты с CDN

HTML-санитизация не требуется — атрибут sandbox обеспечивает полную изоляцию на уровне процесса. Злонамеренные скрипты, формы и навигация блокируются на уровне движка браузера.

Лучшие практики

Дерево решений

terminalbash
Содержимое — богатый HTML с важным оформлением/макетом?
  → ДА: используйте html-preview
  → НЕТ: конвертируйте в markdown

HTML-контент большой (> 1 КБ)?
  → ДА: используйте transform_data, чтобы записать файл, и ссылайтесь через src
  → НЕТ: рассмотрите просто резюме в markdown

Пользователь явно хочет УВИДЕТЬ письмо/HTML?
  → ДА: используйте html-preview (важна визуальная точность)
  → НЕТ: извлеките текстовое содержимое и представьте в markdown

Соглашения по именам

  • Выходные файлы: описательные, kebab-case — newsletter-jan-2026.html, quarterly-report.html
  • Совпадение с контекстом — если пользователь спрашивал о конкретном письме, назовите файл по теме письма

Советы по скриптам

  • Для декодирования писем предпочитайте Python — base64 и json входят в стандартную библиотеку, зависимости не нужны
  • Для Gmail всегда используйте urlsafe_b64decode (никогда b64decode)
  • Используйте рекурсивный паттерн find_html_part() — он обрабатывает все структуры писем
  • Держите скрипты лаконичными — сложную логику сложнее отлаживать при таймауте 30 секунд

Устранение неполадок

«Loading...» показывается бесконечно

  • Путь "src" должен быть абсолютным — используйте точный путь, возвращённый transform_data
  • Не собирайте относительные пути и не угадывайте расположение папки данных
  • Проверьте, что transform_data завершился успешно (посмотрите сообщение результата инструмента)

Пустой/белый iframe

  • HTML-файл может быть пустым — проверьте вывод transform_data на ошибки
  • Base64-декодирование могло молча не сработать — убедитесь, что скрипт корректно обрабатывает структуру письма
  • Проверьте, есть ли в письме HTML-часть (некоторые письма только текстовые)

Изображения не загружаются

  • Изображения с URL http:// могут блокироваться (CSP требует https://)
  • Некоторые изображения писем используют пиксели отслеживания, срок действия которых мог истечь
  • Inline-изображения cid: (Content-ID) не поддерживаются — им нужны MIME-вложения письма

Нечитаемый текст / проблемы с кодировкой

  • Всегда декодируйте в utf-8: .decode('utf-8')
  • Некоторые старые письма используют другие кодировки — проверьте заголовок Content-Type на наличие charset
  • Если charset не UTF-8, декодируйте соответственно: .decode(charset)

HTML отображается как исходный код (не рендерится)

  • Убедитесь, что язык блока кода — html-preview (не html или htm)
  • Проверьте, что JSON-конфигурация валидна: должно быть поле "src"
  • Убедитесь, что содержимое файла — настоящий HTML (не JSON, содержащий HTML)

Ошибки скрипта в transform_data

  • KeyError на payload.parts — письмо может быть одночастным (нет массива parts). Используйте рекурсивный паттерн find_html_part()
  • binascii.Error: Invalid base64 — письмо может использовать стандартный base64, а не URL-safe. Попробуйте base64.b64decode() как запасной вариант
  • UnicodeDecodeError — проверьте кодировку charset письма (см. проблемы с кодировкой выше)

Нужен такой агент в вашей компании?

Устанавливаем под ключ: настройка на компьютерах сотрудников, единый аккаунт, интеграция с 1С и внутренними системами, обучение команды. Подробнее о внедрении →