mcpField
D
datalens-html-pages

datalens-html-pages

github.com/datalens-tech开发1245 次安装3 周前更新
代码仓库

关于 datalens-html-pages

用于编写由 DataLens 在受限沙箱中提供的独立 HTML 报告和页面。它说明资源、安全与交互限制,并提供上传前检查页面的验证脚本。

主要功能

  1. 01编写独立 HTML 页面
  2. 02遵循沙箱与 CSP 限制
  3. 03使用允许列表中的外部资源
  4. 04通过宿主消息处理导出和链接
  5. 05用 Python 脚本验证页面
  6. 06支持主题、语言和移动端

适用场景

  1. 01创建 DataLens 报告页面
  2. 02上传前检查 HTML
  3. 03通过 Yandex Maps JS API 2.1 添加地图
  4. 04准备适配移动端的页面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/datalens-tech/datalens-skills --skill datalens-html-pages

安装后在 Claude Code 中输入/datalens-html-pages确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
dl-dashboard.css
dl-theme-tokens.css
report.template.html
references/
authoring-constraints.md
csp-and-sandbox.md
design-principles.md
publication.md
scripts/
validate_page.py

详细介绍

DataLens HTML 页面

DataLens 通过将独立 HTML 页面(通常是 AI 生成的报告等完整文档)存储在私有存储桶中,并通过短 TTL 预签名 URL在锁定的沙盒 iframe中呈现这些页面,同时在上传时以 <meta> 标签注入 Content-Security-Policy。你需要编写完整的 <!DOCTYPE html> 页面;脚本可以运行,但只能来自允许列表中的 CDN,而且大多数浏览器 API 都会被屏蔽。

不属于此技能:图表内部的 HTML——标记函数、HTML 标记表格单元格、ChartEditor.generateHtml。这是另一项功能,有严格的标签/属性允许列表,且完全不允许脚本。此技能仅适用于独立 HTML 页面。

运行时概览

  • 沙盒 iframe:sandbox="allow-scripts"、allow=""、referrerpolicy="no-referrer",并且至关重要的是不含 allow-same-origin——页面在不透明源中运行(父页面也会发送 Cross-Origin-Opener-Policy: same-origin)。同源隔离依赖于此;切勿假设可以进行同源访问。
  • 注入的 CSP(你无需编写,平台会以幂等方式将其置于页面开头): default-src 'none',并为 script/style/font/img/media 提供一个小型允许列表;connect-src 仅对 Yandex Maps 主机开放;form-action、frame-src、object-src、worker-src、base-uri 均为 'none'。 完整策略见 references/csp-and-sandbox.md。
  • 页面由预签名 GET 提供,该 URL 会在 10–30 秒后过期,且须先通过服务端权限检查。主题/语言会作为签名查询参数一并传递。

最终效果:**让页面完全自包含。**将自己的 CSS/JS 内联(CSP 允许 'unsafe-inline'/'unsafe-eval'),仅从允许列表中的 CDN 加载库,绝不依赖网络、存储或父页面。

编写规则

允许的外部资源(其他资源都会被 CSP 屏蔽):

资源允许来源
脚本cdn.jsdelivr.net、cdnjs.cloudflare.com、cdn.tailwindcss.com、yastatic.net、Yandex Maps(api-maps.yandex.ru、*.api-maps.yandex.ru、*.maps.yandex.net、suggest-maps.yandex.ru)(以及内联脚本)
样式上述 CDN 以及 fonts.googleapis.com、blob:(以及内联样式)
字体cdn.jsdelivr.net、cdnjs.cloudflare.com、fonts.gstatic.com、data:
图片yastatic.net、Yandex Maps 主机、data:、blob:
媒体仅 data:、blob:
网络(connect-src)仅 Yandex Maps 主机——供 Maps API 本身使用,不能用于你的数据

按标准方式加载 Google Fonts(通过 fonts.googleapis.com 加载 <link> CSS,字体文件来自 fonts.gstatic.com)。将允许列表之外的库(unpkg、plot.ly、d3js.org、jQuery)镜像到 jsdelivr/cdnjs。

Yandex Maps JS API 2.1 开箱即用:从 api-maps.yandex.ru 加载(如果用户有 API key,则使用该密钥),并在固定高度的容器中渲染。脚本、地图瓦片和 API 自身的请求均被允许。使用 2.1,不要用 v3——v3 没有密钥就无法加载,并且需要沙盒会屏蔽的 worker。沙盒的其他限制仍然适用——你自己的数据必须内联,不能通过请求获取。

<script src="https://api-maps.yandex.ru/2.1/?apikey=YOUR_KEY&lang=ru_RU"></script>
<div id="map" style="height: 400px"></div>
<script>ymaps.ready(() => new ymaps.Map('map', {center: [55.76, 37.64], zoom: 10}));</script>

已屏蔽——请勿使用(这些操作会报错或静默失败):

  • 存储:localStorage、sessionStorage、indexedDB、document.cookie、Cache API → 将所有状态保存在内存中。
  • **网络:**除 Yandex Maps 主机外,禁止将 fetch、XMLHttpRequest、WebSocket、EventSource、sendBeacon 用于任何对象——connect-src 不允许其他连接。将数据内联到页面中,不要通过请求获取。
  • **框架/对象/表单:**嵌套的 <iframe>、<object>、<embed>、提交 <form>、<base>。
  • Workers、弹出窗口、对话框、下载、摄像头/地理位置/全屏功能、导航父框架,以及普通的链接导航——<a href> 不会自行打开;请通过 OPEN_URL 处理点击(参见下文的打开链接)。

主题和语言——从查询字符串中读取:

const theme = new URLSearchParams(location.search).get('theme'); // 'light' | 'dark' | 'system'
const lang  = new URLSearchParams(location.search).get('lang');  // 'ru' | 'en'

设计和生成 HTML 时,请尽量创建支持可用主题和语言且能自适应的页面:部分用户会通过 DataLens 的移动网页版查看页面。

导出文件 — 下载 API 会被阻止;请将数据交给宿主处理。宿主会验证 event.source,应用 MIME 类型白名单和大小上限,清理文件名,然后创建下载:

parent.postMessage({ code: 'EXPORT', data: { name: 'report.csv', mime: 'text/csv', data: csvString } }, '*');

打开链接 — 普通的 <a href> 不会在沙箱内导航(源不透明;即使设置 target="_blank",也只会打开 about:blank)。要打开 URL,请向宿主发出请求:拦截点击、调用 preventDefault(),然后发送 OPEN_URL。一个委托监听器即可处理所有链接 — 仅在页面嵌入框架时启用,这样文件在顶层打开或在本地预览时仍能正常工作。

if (window.parent !== window) {
  document.addEventListener('click', (e) => {
    const a = e.target.closest('a[href]');
    if (!a) return;
    const href = a.getAttribute('href');
    if (!href || href.startsWith('#')) return; // in-page anchors scroll normally — leave them
    e.preventDefault();
    parent.postMessage({ code: 'OPEN_URL', data: { url: a.href } }, '*');
  });
}

编码和大小(上传时强制检查):

  • 使用有效的 UTF-8;请在前 1024 字节内声明 <meta charset="utf-8">。服务器会检测乱码,但不会修复。
  • 不要用 Markdown 代码围栏包裹文档(```html)— 这会提前关闭 <head>。
  • 页面大小最多约 10 MB,最好控制在约 5 MB 以下。

详尽列表请参阅 references/authoring-constraints.md,合规的起始模板请参阅 assets/report.template.html。

设计 — 构建仪表板或报告页面前,请询问用户是否希望遵循 DataLens 设计原则进行样式设计(除非用户已经说明)。如果无法询问 — 例如在无法获取答复的非交互式运行中 — 默认不应用这些原则:采用简洁朴素的样式,并在输出中说明未采用设计原则。如果用户同意,请遵循 references/design-principles.md — 包括布局网格、图表类型选择、颜色、数字格式和主题。该指南依赖 assets/dl-theme-tokens.css 中的 DataLens 设计令牌,以及 assets/dl-dashboard.css 中的基础仪表板样式 — 请将这两个文件的内容都内联到页面的 <style> 中(页面必须是独立的),并根据 theme 查询参数,在根元素上设置 g-root g-root_theme_light|dark。如果用户拒绝,请跳过这三个文件,按用户要求为页面设置样式;但仍须遵守上述沙箱和 CSP 规则。

上传前验证

根据确切的 CSP 白名单和沙箱限制检查页面 — 检查通过意味着渲染时不会有任何内容被阻止或剥离:

python scripts/validate_page.py report.html          # errors + warnings
python scripts/validate_page.py --strict report.html # warnings fail too (CI mode)
cat report.html | python scripts/validate_page.py -   # from stdin

该检查器会标记不在白名单中的资源主机、被阻止的 API(存储、fetch、workers 等)、被阻止的标签、缺少字符集、乱码、被代码围栏包裹的文档以及超大页面。运行 python scripts/validate_page.py --self-test 可检查检查器本身。

提供和发布

要上传或发布页面并返回永久链接,请遵循 references/publication.md。

页面由服务器端提供;请了解其工作方式,以便正确编写页面:

  • 预签名 GET,有效期 10–30 秒,仅在权限检查通过后生成(不存在 TOCTOU 时间差)。不要构建依赖长期有效 URL 的流程。
  • 对象存储时使用 Content-Type: text/html; charset=utf-8、Content-Disposition: inline 和 Cache-Control: no-store。上传通过服务器端 JSON-RPC 完成(不使用预签名 PUT — CSP 注入和元数据由服务器负责)。
  • iframe 会携带上述沙箱属性;父级会发送 COOP: same-origin。

需要在设计中考虑的已接受风险(详情见 references/csp-and-sandbox.md):在顶层打开的 URL(在其有效期内)会失去 iframe 沙箱保护,但仍保留注入的 CSP;页面可以通过 <meta http-equiv="refresh"> 自行导航;白名单中的 CDN 未通过 SRI 固定。由于框架采用同源隔离且无法访问网络,请勿将必须保密的查看者数据、参数或实时 LLM 输出放入页面 — 这只是报告,不是可信的应用界面。

常见问题 → 解决方法

症状原因解决方法
库始终无法加载/控制台出现 CSP 错误脚本/样式托管地址不在允许列表中从 jsdelivr / cdnjs / tailwind / yastatic 提供
数据始终不显示代码调用了 fetch/XHR在生成页面时将数据集内联到页面中
“localStorage is not available”/抛出异常沙箱中使用了存储 API在内存中保留状态
字体无法显示字体托管地址不在允许列表中使用 Google Fonts(fonts.googleapis.com CSS + fonts.gstatic.com 文件)或 jsdelivr/cdnjs
图片空白使用了 http:// 或不在允许列表中的托管地址使用 yastatic.net、data: 或 blob:
Yandex 地图空白使用了 Maps v3、从镜像加载 API,或容器没有高度使用来自 api-maps.yandex.ru 的 JS API 2.1,并为容器设置明确的高度
下载按钮不起作用下载 API 被阻止向父级发送 {code:'EXPORT', data:{name,mime,data}}
点击链接不起作用链接无法在沙箱中跳转拦截点击并发送 {code:'OPEN_URL', data:{url}}
页面显示为纯文本/head 损坏Markdown 代码围栏嵌套移除 ``` 围栏
上传被拒绝超过 10 MB 或 UTF-8 无效缩小资源;确保使用 UTF-8,并尽早添加 <meta charset>

评论

支持 Markdown