Web Device 会把一小部分独立的 Markdown 链接识别为内容嵌入。这不是把任意网站放进 frame 的通用权限。当前 content reader 识别 YouTube 视频 URL、X/Twitter status URL 与经过校验的同站绝对路径;其他 URL 仍会以可见链接保留。
本页是 feature live 指南。原子 directive 合同见 embed unit。矩阵单行看 unit 页;如何用 + 可运行 frame 看本页。
当 source 本身也是文档的一部分时,使用显式的 embed directive。它能明确表达作者意图,并把 source 直接放在渲染结果上方。
Sample 合同
| 字段 | 值 |
|---|---|
| 用途 | 内容 Markdown 中受支持的第三方与同站嵌入 |
| 边界 | 不是通用 iframe 能力;对本站内容/AUP state 文档保持只读 |
| 作者入口 | ::embed{url=…} 或独立的受识别 URL |
| 表面 | Web Device document → 带 provider 约束的 frame/media |
| 公共状态 | YouTube、X status、校验后的内部路径为 released |
| Fallback / 失败 | 未识别或被拒绝的 URL → 普通链接 / 无 embed descriptor |
| 验证 | 下列路由 · arc --version = 2.0.0-beta.32 · 内部路径仅同站 host |
一个 YouTube 嵌入
源代码:
::embed{url="https://youtu.be/jNQXAC9IVRw"}渲染结果:
当前 renderer 会把这个受支持的 URL 变成响应式、延迟加载、受 sandbox 约束的第三方 iframe。文档仍是只读的:frame 中不是这个 Markdown 对象的 editor,也不是由文档拥有的 AUP session。只读指本站内容与 AUP state,不代表第三方 frame 不会进行自身的网络行为。
一条 X 帖子
源代码:
::embed{url="https://x.com/arcblock_io/status/1955367769672950142"}渲染结果:
renderer 会保留原始 status URL 作为 frame 的 fallback target。嵌入帖子的可用性和外观仍由 X 决定;不要把第三方 frame 当作文档已经持久化状态或授予能力的证据。
一个同站页面或 deck
使用以单个 / 开头的同站绝对路径,嵌入本站的一个页面。客户端 slides widget 初始化完成后,slides 内容对象会以 16 的播放器表面呈现;在此之前,frame 内仍保留它 server-rendered 的文字稿。直接打开 source 路由时,正常的可阅读文字稿仍会保留。
源代码:
::embed{url="/zh/docs/web-device/slides-demo/"}渲染结果:
路径必须留在本站内。protocol-relative path、traversal segment、反斜杠与 control character 都会被拒绝;这条 directive 不会授予通用的外部 iframe 能力。query 与 fragment 可以选择目标页面自身支持的状态,例如 deck 的 ?full#slide-… 链接。deck 合同见Markdown Slides。
同站 embed 会把目标页放进 iframe。ARC 默认 frame-ancestors 'none' 会拦截这种 framing。要嵌入本站页面,站点必须配置 .web/security:
frame-ancestors: "'self'"没有它时,iframe 空白,而目标 URL 单独打开仍然正常。本站的 deck 与其它内部 embed 依赖该覆盖。
拒绝路径与普通链接 fallback
当 URL 为空、不是受识别 provider、无法解析、使用非 http(s) scheme,或未通过内部路径检查时,resolveContentEmbed 不返回 descriptor。在内容 Markdown 中这些情况保持为普通链接(或纯文本),而不是 frame。
| 输入 | 预期结果 |
|---|---|
| 受支持的 YouTube / X status HTTPS URL | Provider embed frame |
无 ..、\、control char 的站点路径 /zh/docs/…/ | 同站 internal embed |
//evil.example/(protocol-relative) | 作为内部路径被拒绝 → 无 embed |
/../secret 或含 .. 的 path segment | 拒绝(traversal) |
含 control character 或 \ 的路径 | 拒绝 |
https://example.com/not-a-provider | 无 resolver 匹配 → 普通链接 |
| 句子中的内联链接 | 始终保持普通链接 |
不要为了“证明失败”而在本页把被拒绝路径写成 live ::embed:失败形态是“无 embed”,不是特殊错误外壳。拒绝用例写在上方的 source fence 中,不要做成 live frame。
带上下文的文字保持为链接
只有独立的受支持 URL 或显式 directive 会变成 embed。出现在句子中的链接仍是普通链接:
继续之前,先阅读[原帖](https://x.com/arcblock_io/status/1955367769672950142)。渲染结果:
继续之前,先阅读原帖。
内容对象和语言版本的精确目录结构见内容对象、元数据与语言;本页记录受识别的外部 provider 与经过校验的同站路径。使用通用 frame 前,先针对它的 target renderer 核验安全与信任边界。
验证证据
| 项 | 值 |
|---|---|
| 基线 | 2026-08-12 · arc --version → 2.0.0-beta.32 |
| 能力行 | docs/aup/capability-matrix.md (internal, non-routed) · ::embed |
| 实现 | providers/runtime/web-device/src/content-embed.ts(resolveContentEmbed、checkInternalEmbedPath);content-reader.ts embed 分支 |
| 站点 framing | .web/security → frame-ancestors: "'self'"(同站 iframe) |
| unit 页 | embed |
| 公开路由 | /zh/docs/web-device/markdown-embeds/ · /en/docs/web-device/markdown-embeds/ |
| 本地运行 | arc service start --blocklet blocklets/arcblock --port 4900 · 确认 YouTube 与同站 frame;X 依赖第三方可用性 |
| 验收 | 受支持 URL 成 frame(非空白);不受支持保持链接;内部路径仅同站 |