页面收藏和划词笔记是 blocklet 级能力。省略 save: 即开启。Chrome 在访客通过身份验证之前不出现。这不是页面内评审(?review=1),不是评论,也不是 connect-service User Center。
本页证据是 Arc feat/save-capability 上的解析器与运行时(arc#6715,PR #6716):packages/core/src/blocklet/save-capability.ts、parse-manifest.ts 的 save:、packages/aup/src/save-surface.ts、packages/aos/src/session/bookmark.ts、annotate.ts。它不是已发布的 arc --version tag。不要把它当成旧 host 上已有的合同。
动机见:收藏是运行时能力,不是站点插件。
如何声明
写在 blocklet 的 blocklet.yaml 上,不要写进 .web/site.yaml,也不要写在某一页。
| 输入 | 规范化 { enabled, chrome } | HTML data-arc-save-capability |
|---|---|---|
省略 / null / true | { enabled: true, chrome: "floating" } | floating |
false | { enabled: false, chrome: "floating" } | off |
{ enabled: false } | { enabled: false, chrome: "floating" } | off |
{ chrome: "buttons" } | { enabled: true, chrome: "buttons" } | buttons |
{ enabled: true, chrome: "floating" } | 同左 | floating |
# 默认:省略该字段
save: false
save:
enabled: false
save:
chrome: buttons字段参考
| 键 | 类型 | 默认 | 说明 |
|---|---|---|---|
save | boolean 或对象 | 省略 = 开 | blocklet.yaml 顶层。spec v1 与 v2。 |
save.enabled | boolean | 除非值为恰好 false,否则 true | 只有 false 才关闭。对象里缺这个键仍开启。 |
save.chrome | "floating" | "buttons" | "floating" | 规范化时其它字符串当 floating。解析器拒绝未知枚举。 |
| 对象多余键 | 无 | 无 | 拒绝(.strict())。save: { chrome: floating, extra: 1 } 解析失败。 |
布尔 save: true 可写,等于默认。
登录门闩
| 信号 | 效果 |
|---|---|
save 关闭(data-arc-save-capability="off") | 无 chrome。页面上的 [data-arc-save] 保持隐藏(html:not([data-arc-save-ready]) [data-arc-save] { display: none })。 |
无 session 或 authenticated: false | chrome 与关闭相同。声明本身不变。 |
authenticated: true 且能力开启 | 把 data-arc-save-ready 设为 floating 或 buttons,并挂上对应模式。 |
未登录时,若仍点到收藏控件:点 header 的 Login,或跳 /login?redirect=<当前 path+query>。
Web Device 通过 window.__arcSessionCtx / __arcSessionWaiters 发布 session。AUP 在 blocklet 设置了 save 时,在 render/stage 帧上带 msg.save(off | floating | buttons)。客户端先把它写到 window.__arcSave 和 HTML 属性,再排空 waiter。
共享 IIFE 的覆盖顺序:window.__arcSave(字符串或 { enabled, chrome })优先于 HTML 属性。两者都缺 → floating。
展示模式
floating(默认)
登录后出现侧栏(.arc-save-bar,data-arc-save="bar"):
| 控件 | data-arc-save | 动作 |
|---|---|---|
| Bookmark | bookmark | exec("/.actions/bookmark", { url, title }) |
| My saves | library | 跳到 /{locale}/my/ |
| 划词 | (mouseup) | 选中文本长度 ≥ 2 时打开笔记框 → /.actions/annotate |
url 是 window.location.href。title 在非空时用 document.title。
My saves 的 locale:若 location.pathname 匹配 ^/([a-z]{2}(?:-[A-Za-z0-9]+)?)/,用 /{该段}/my/。否则 /my/。
buttons
无侧栏。无自动划词弹层。绑定页面自己的控件:
data-arc-save | 本模式是否必需 | 已登录时的行为 |
|---|---|---|
bookmark | 是 | 同上 bookmark exec;设置 aria-pressed |
annotate | 可选 | 打开笔记框(有选区就用选区) |
library | 可选 | 去 /{locale}/my/ |
bar | 仅运行时 | 不当点击目标 |
<button type="button" data-arc-save="bookmark">Bookmark</button>
<button type="button" data-arc-save="annotate">Annotate</button>
<a data-arc-save="library">My saves</a>点击向上冒泡:从 event target 往上走到第一个不是 bar 的 data-arc-save。
HTML 属性
| 属性 | 位置 | 取值 | 谁写入 |
|---|---|---|---|
data-arc-save-capability | <html> | off | floating | buttons | Web Device finalizePageHtml / stampSaveCapabilityHtml。AUP 在 msg.save 为这三者之一时由 handleAup 写入。 |
data-arc-save-ready | <html> | floating | buttons | 客户端,仅在已登录且能力开启后。关闭或匿名时移除。 |
data-arc-save | 控件 | bar | bookmark | annotate | library | 浮动栏(运行时)或页面作者(buttons 模式)。 |
盖章幂等:<html> 上已有 data-arc-save-capability 则不再改写。属性加在标签结束符之前,不打乱 lang / data-tone。
Actions
两个 action 都写入本 session 调用者的 DID Space。线上传入的 path / target / collection 被忽略。Workbench 私有字段(tagIds、status、collectionIds、dataVersion、version)被拒绝。
/.actions/bookmark
| 参数 | 必填 | 说明 |
|---|---|---|
url | 是 | 绝对 http: 或 https: URL。成为 objectId。 |
title | 否 | 记录 name。无标题时从 URL 派生标签(最后一段 path 或 hostname)。标签不是出处。 |
ctype / contentType / schemaType | 否 | 只允许 ABOUT 类型:link(默认)、place、product。未知声称类型直接拒绝,不会改写成 link。 |
notes / text / description | 否 | L1 记录上的可选正文。浮动栏不发送这些字段。 |
写入路径:/user/bookmarks/{id},id = b_ + SHA-256(url) 前 16 字节的 hex。
成功:{ ok: true, id, path, copied }。同一 URL 再存:copied: false(同一 id)。
error.code | 何时 |
|---|---|
unauthorized | 无 callerDid,或 /user 写入被拒 / 找不到 |
invalid-url | 缺省或不是绝对 http(s) |
unknown-type | 声称类型不是 link / place / product |
invalid-record | 内置 schema 校验失败 |
workbench-private-field | payload 仍带 Aside 私有字段 |
/.actions/annotate
| 参数 | 必填 | 说明 |
|---|---|---|
url | 是 | 绝对 http(s) 页面 URL → isBasedOn |
quote | 除非有 text | 选中文本 → 记录 text |
text | quote 的别名 | quote 为空时使用 |
note | 否 | 可选;存为记录 name |
写入路径:/user/notes/{id},id = n_ + SHA-256(url + "\n" + quote + "\n" + note) 前 16 字节 hex。contentType: note。
error.code | 何时 |
|---|---|
unauthorized | 无 caller,或 /user 写入被拒 |
invalid-url | 缺省或不是绝对 http(s) |
invalid-quote | quote 与 text 都空 |
invalid-record | schema 校验失败 |
workbench-private-field | 带了 Aside 私有字段 |
浮动栏只在当前选区长度 ≥ 2 时打开笔记框。
/my/ 组合页
内置路由:/{locale}/my/(locale 规则与 My saves 相同)。不是 /.well-known/service/user。
save 开启时,段落固定先是:
| id | label | path |
|---|---|---|
bookmarks | Bookmarks | /user/bookmarks |
notes | Notes | /user/notes |
然后是声明了 faces.my: true 并且活在 /user 下的 collection。重复的 id 或 path 跳过。composeMyPageSections 传入 { save: false } 时(运行时在 save.enabled 为 false 时这样做):内置书签/笔记段消失;额外的 faces.my 集合仍在。
失败与边界
| 边界 | 行为 |
|---|---|
解析未知 save.chrome | Manifest 解析抛错(enum) |
| 解析未知对象键 | Manifest 解析抛错(strict) |
| 规范化垃圾值(数组、随意对象) | 运行时当默认开 / floating,除非 enabled: false |
| 匿名访客 | 无 chrome;[data-arc-save] 隐藏 |
save: false | 属性 off;/my/ 去掉内置段 |
| 未登录调 action | unauthorized |
非 http(s) url | invalid-url |
| floating 模式下选区长度 0 或 1 | 无弹层 |
| 跨站阅读列表 | 不是这个能力。记录是这个站上该调用者的 /user/** |
| 页面内评审 | 另一套 overlay。见 页面内评审模式 |
相关页面
- 限制谁能读一页:公开 HTML 与登录后写入
- 页面内评审模式:写作时 overlay,不是读者收藏
- Manifest 与能力:包上的
save: - 介绍文章:收藏是运行时能力,不是站点插件